提交 c3cf0f74 authored 作者: chenshiying's avatar chenshiying

[新增] nacos 版本升级2.0.3

上级 cceef1d1
# Spring Cloud Alibaba
[![CircleCI](https://circleci.com/gh/alibaba/spring-cloud-alibaba/tree/master.svg?style=svg)](https://circleci.com/gh/alibaba/spring-cloud-alibaba/tree/master)
[![Maven Central](https://img.shields.io/maven-central/v/com.alibaba.cloud/spring-cloud-alibaba-dependencies.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:com.alibaba.cloud%20AND%20a:spring-cloud-alibaba-dependencies)
[![Codecov](https://codecov.io/gh/alibaba/spring-cloud-alibaba/branch/master/graph/badge.svg)](https://codecov.io/gh/alibaba/spring-cloud-alibaba)
[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
A project maintained by Alibaba.
See the [中文文档](https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md) for Chinese readme.
Spring Cloud Alibaba provides a one-stop solution for distributed application development. It contains all the components required to develop distributed applications, making it easy for you to develop your applications using Spring Cloud.
With Spring Cloud Alibaba, you only need to add some annotations and a small amount of configurations to connect Spring Cloud applications to the distributed solutions of Alibaba, and build a distributed application system with Alibaba middleware.
## Features
* **Flow control and service degradation**: Flow control for HTTP services is supported by default. You can also customize flow control and service degradation rules using annotations. The rules can be changed dynamically.
* **Service registration and discovery**: Service can be registered and clients can discover the instances using Spring-managed beans, auto integration Ribbon.
* **Distributed configuration**: Support for externalized configuration in a distributed system, auto refresh when configuration changes.
* **Event-driven**: Support for building highly scalable event-driven microservices connected with shared messaging systems.
* **Distributed Transaction**: Support for distributed transaction solution with high performance and ease of use.
* **Alibaba Cloud Object Storage**: Massive, secure, low-cost, and highly reliable cloud storage services. Support for storing and accessing any type of data in any application, anytime, anywhere.
* **Alibaba Cloud SchedulerX**: Accurate, highly reliable, and highly available scheduled job scheduling services with response time within seconds.
* **Alibaba Cloud SMS**: A messaging service that covers the globe, Alibaba SMS provides convenient, efficient, and intelligent communication capabilities that help businesses quickly contact their customers.
For more features, please refer to [Roadmap](https://github.com/alibaba/spring-cloud-alibaba/blob/master/Roadmap.md).
In addition to the above-mentioned features, for the needs of enterprise users' scenarios, [Microservices Engine (MSE)](https://www.aliyun.com/product/aliware/mse?spm=github.spring.com.topbar) of Spring Cloud Alibaba's enterprise version provides an enterprise-level microservices governance center, which includes more powerful governance capabilities such as Grayscale Release, Service Warm-up, Lossless Online and Offline and Outlier Ejection. At the same time, it also provides a variety of products and solutions such as enterprise-level Nacos registration / configuration center, enterprise-level cloud native gateway.
## Components
**[Sentinel](https://github.com/alibaba/Sentinel)**: Sentinel takes "traffic flow" as the breakthrough point, and provides solutions in areas such as flow control, concurrency, circuit breaking, and load protection to protect service stability.
**[Nacos](https://github.com/alibaba/Nacos)**: An easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications.
**[RocketMQ](https://rocketmq.apache.org/)**: A distributed messaging and streaming platform with low latency, high performance and reliability, trillion-level capacity and flexible scalability.
**[Dubbo](https://github.com/apache/dubbo)**: A high-performance, Java based open source RPC framework.
**[Seata](https://github.com/seata/seata)**: A distributed transaction solution with high performance and ease of use for microservices architecture.
**[Alibaba Cloud ACM](https://www.aliyun.com/product/acm)**: An application configuration center that enables you to centralize the management of application configurations, and accomplish real-time configuration push in a distributed environment.
**[Alibaba Cloud OSS](https://www.aliyun.com/product/oss)**: An encrypted and secure cloud storage service which stores, processes and accesses massive amounts of data from anywhere in the world.
**[Alibaba Cloud SMS](https://www.aliyun.com/product/sms)**: A messaging service that covers the globe, Alibaba SMS provides convenient, efficient, and intelligent communication capabilities that help businesses quickly contact their customers.
**[Alibaba Cloud SchedulerX](https://www.aliyun.com/aliware/schedulerx?spm=5176.10695662.784137.1.4b07363dej23L3)**: Accurate, highly reliable, and highly available scheduled job scheduling services with response time within seconds..
For more features please refer to [Roadmap](https://github.com/alibaba/spring-cloud-alibaba/blob/master/Roadmap.md).
## How to build
* **2020.0 branch**: Corresponds to Spring Cloud 2020 & Spring Boot 2.4.x. JDK 1.8 or later versions are supported.
* **master branch**: Corresponds to Spring Cloud Hoxton & Spring Boot 2.2.x. JDK 1.8 or later versions are supported.
* **greenwich branch**: Corresponds to Spring Cloud Greenwich & Spring Boot 2.1.x. JDK 1.8 or later versions are supported.
* **finchley branch**: Corresponds to Spring Cloud Finchley & Spring Boot 2.0.x. JDK 1.8 or later versions are supported.
* **1.x branch**: Corresponds to Spring Cloud Edgware & Spring Boot 1.x, JDK 1.7 or later versions are supported.
Spring Cloud uses Maven for most build-related activities, and you should be able to get off the ground quite quickly by cloning the project you are interested in and typing:
```bash
./mvnw install
```
## How to Use
### Add maven dependency
These artifacts are available from Maven Central and Spring Release repository via BOM:
```xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.7.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
```
add the module in `dependencies`.
### Reference Doc
[Contents](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/spring-cloud-alibaba.adoc)
[Nacos Config](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config.adoc)
[Nacos Discovery](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-discovery.adoc)
## Examples
A `spring-cloud-alibaba-examples` module is included in our project for you to get started with Spring Cloud Alibaba quickly. It contains an example, and you can refer to the readme file in the example project for a quick walkthrough.
Examples:
[Sentinel Example](https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme.md)
[Nacos Config Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme.md)
[Nacos Discovery Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme.md)
[RocketMQ Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/rocketmq-example/readme.md)
[Alibaba Cloud OSS Example](https://github.com/alibaba/aliyun-spring-boot/tree/master/aliyun-spring-boot-samples/aliyun-oss-spring-boot-sample)
[Dubbo Spring Cloud Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/README_CN.md)
## Version control guidelines
The version number of the project is in the form of x.x.x, where x is a number, starting from 0, and is not limited to the range 0~9. When the project is in the incubator phase, the version number is 0.x.x.
As the interfaces and annotations of Spring Boot 1 and Spring Boot 2 have been changed significantly in the Actuator module, and spring-cloud-commons is also changed quite a lot from 1.x.x to 2.0.0, we take the same version rule as SpringBoot version number.
* 1.5.x for Spring Boot 1.5.x
* 2.0.x for Spring Boot 2.0.x
* 2.1.x for Spring Boot 2.1.x
* 2.2.x for Spring Boot 2.2.x
* 2020.x for Spring Boot 2.4.x
## Code of Conduct
This project is a sub-project of Spring Cloud, it adheres to the Contributor Covenant [code of conduct](https://github.com/spring-cloud/spring-cloud-build/blob/master/docs/src/main/asciidoc/code-of-conduct.adoc). By participating, you are expected to uphold this code. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
## Code Conventions and Housekeeping
None of these is essential for a pull request, but they will all help. They can also be added after the original pull request but before a merge.
Use the Spring Framework code format conventions. If you use Eclipse you can import formatter settings using the eclipse-code-formatter.xml file from the Spring Cloud Build project. If using IntelliJ, you can use the Eclipse Code Formatter Plugin to import the same file.
Make sure all new .java files to have a simple Javadoc class comment with at least an @author tag identifying you, and preferably at least a paragraph on what the class is for.
Add the ASF license header comment to all new .java files (copy from existing files in the project)
Add yourself as an @author to the .java files that you modify substantially (more than cosmetic changes).
Add some Javadocs and, if you change the namespace, some XSD doc elements.
A few unit tests would help a lot as well —— someone has to do it.
If no-one else is using your branch, please rebase it against the current master (or other target branch in the main project).
When writing a commit message please follow these conventions, if you are fixing an existing issue please add Fixes gh-XXXX at the end of the commit message (where XXXX is the issue number).
## Contact Us
Mailing list is recommended for discussing almost anything related to spring-cloud-alibaba.
spring-cloud-alibaba@googlegroups.com: You can ask questions here if you encounter any problem when using or developing spring-cloud-alibaba.
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<groupId>com.timeloit.cloud</groupId> <groupId>com.timeloit.cloud</groupId>
<artifactId>spring-cloud-timeloit</artifactId> <artifactId>spring-cloud-timeloit</artifactId>
<version>2.3.3-SNAPSHOT</version> <version>2.2.8-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>Spring Cloud Timeloit</name> <name>Spring Cloud Timeloit</name>
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
<properties> <properties>
<!-- Project revision --> <!-- Project revision -->
<revision>2.3.3-SNAPSHOT</revision> <revision>2.2.8-SNAPSHOT</revision>
<!-- Dependency Versions --> <!-- Dependency Versions -->
<spring-cloud-commons.version>2.2.5.RELEASE</spring-cloud-commons.version> <spring-cloud-commons.version>2.2.5.RELEASE</spring-cloud-commons.version>
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
<junit.version>4.12</junit.version> <junit.version>4.12</junit.version>
<javax-servlet-api>3.0</javax-servlet-api> <javax-servlet-api>3.0</javax-servlet-api>
<slf4j-api.version>1.7.25</slf4j-api.version> <slf4j-api.version>1.7.30</slf4j-api.version>
<!-- Apache Dubbo --> <!-- Apache Dubbo -->
<dubbo.version>2.7.8</dubbo.version> <dubbo.version>2.7.8</dubbo.version>
...@@ -173,7 +173,7 @@ ...@@ -173,7 +173,7 @@
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.rocketmq</groupId> <groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId> <artifactId>rocketmq-spring-boot-starter</artifactId>
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
<groupId>com.timeloit.cloud</groupId> <groupId>com.timeloit.cloud</groupId>
<artifactId>spring-cloud-timeloit-dependencies</artifactId> <artifactId>spring-cloud-timeloit-dependencies</artifactId>
<version>2.3.3-SNAPSHOT</version> <version>2.2.8-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>Spring Cloud Timeloit</name> <name>Spring Cloud Timeloit</name>
...@@ -21,14 +21,14 @@ ...@@ -21,14 +21,14 @@
<properties> <properties>
<revision>2.3.3-SNAPSHOT</revision> <revision>2.2.8-SNAPSHOT</revision>
<sentinel.version>1.8.0</sentinel.version> <sentinel.version>1.8.1</sentinel.version>
<oss.version>3.1.0</oss.version> <oss.version>3.1.0</oss.version>
<seata.version>1.3.0</seata.version> <seata.version>1.3.0</seata.version>
<nacos.client.version>1.2.1</nacos.client.version> <nacos.client.version>2.0.3</nacos.client.version>
<nacos.config.version>0.8.0</nacos.config.version> <nacos.config.version>0.8.0</nacos.config.version>
<spring.context.support.version>1.0.9</spring.context.support.version> <spring.context.support.version>1.0.11</spring.context.support.version>
<!-- Maven Plugin Versions --> <!-- Maven Plugin Versions -->
<maven-source-plugin.version>2.2.1</maven-source-plugin.version> <maven-source-plugin.version>2.2.1</maven-source-plugin.version>
...@@ -190,6 +190,12 @@ ...@@ -190,6 +190,12 @@
<!-- Own dependencies --> <!-- Own dependencies -->
<dependency>
<groupId>com.timeloit.cloud</groupId>
<artifactId>spring-cloud-timeloit-commons</artifactId>
<version>${revision}</version>
</dependency>
<dependency> <dependency>
<groupId>com.timeloit.cloud</groupId> <groupId>com.timeloit.cloud</groupId>
<artifactId>spring-cloud-circuitbreaker-sentinel</artifactId> <artifactId>spring-cloud-circuitbreaker-sentinel</artifactId>
...@@ -330,4 +336,4 @@ ...@@ -330,4 +336,4 @@
</plugins> </plugins>
</build>--> </build>-->
</project> </project>
\ No newline at end of file
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>spring-cloud-timeloit</artifactId> <artifactId>spring-cloud-timeloit</artifactId>
<groupId>com.timeloit.cloud</groupId> <groupId>com.timeloit.cloud</groupId>
<version>2.3.3-SNAPSHOT</version> <version>2.2.8-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
<module>spring-cloud-timeloit-sentinel-datasource</module> <module>spring-cloud-timeloit-sentinel-datasource</module>
<module>spring-cloud-timeloit-sentinel-gateway</module> <module>spring-cloud-timeloit-sentinel-gateway</module>
<module>spring-cloud-starter-timeloit-sentinel</module> <module>spring-cloud-starter-timeloit-sentinel</module>
<module>spring-cloud-timeloit-commons</module>
</modules> </modules>
</project> </project>
\ No newline at end of file
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>spring-cloud-timeloit-starters</artifactId> <artifactId>spring-cloud-timeloit-starters</artifactId>
<groupId>com.timeloit.cloud</groupId> <groupId>com.timeloit.cloud</groupId>
<version>2.3.3-SNAPSHOT</version> <version>2.2.8-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
...@@ -62,4 +62,4 @@ ...@@ -62,4 +62,4 @@
</dependencies> </dependencies>
</project> </project>
\ No newline at end of file
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>spring-cloud-timeloit-starters</artifactId> <artifactId>spring-cloud-timeloit-starters</artifactId>
<groupId>com.timeloit.cloud</groupId> <groupId>com.timeloit.cloud</groupId>
<version>2.3.3-SNAPSHOT</version> <version>2.2.8-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
...@@ -51,15 +51,10 @@ ...@@ -51,15 +51,10 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-test-support</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId> <artifactId>spring-boot-starter</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
</project>
\ No newline at end of file </project>
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>spring-cloud-timeloit-starters</artifactId> <artifactId>spring-cloud-timeloit-starters</artifactId>
<groupId>com.timeloit.cloud</groupId> <groupId>com.timeloit.cloud</groupId>
<version>2.3.3-SNAPSHOT</version> <version>2.2.8-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
...@@ -87,12 +87,6 @@ ...@@ -87,12 +87,6 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-test-support</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.powermock.modules.test.powermockito/powermock-modules-test-powermockito --> <!-- https://mvnrepository.com/artifact/org.powermock.modules.test.powermockito/powermock-modules-test-powermockito -->
<dependency> <dependency>
<groupId>org.powermock</groupId> <groupId>org.powermock</groupId>
...@@ -110,5 +104,4 @@ ...@@ -110,5 +104,4 @@
</dependencies> </dependencies>
</project>
</project>
\ No newline at end of file
...@@ -53,6 +53,7 @@ import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT_PORT; ...@@ -53,6 +53,7 @@ import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT_PORT;
import static com.alibaba.nacos.api.PropertyKeyConst.MAX_RETRY; import static com.alibaba.nacos.api.PropertyKeyConst.MAX_RETRY;
import static com.alibaba.nacos.api.PropertyKeyConst.NAMESPACE; import static com.alibaba.nacos.api.PropertyKeyConst.NAMESPACE;
import static com.alibaba.nacos.api.PropertyKeyConst.PASSWORD; import static com.alibaba.nacos.api.PropertyKeyConst.PASSWORD;
import static com.alibaba.nacos.api.PropertyKeyConst.RAM_ROLE_NAME;
import static com.alibaba.nacos.api.PropertyKeyConst.SECRET_KEY; import static com.alibaba.nacos.api.PropertyKeyConst.SECRET_KEY;
import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR; import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR;
import static com.alibaba.nacos.api.PropertyKeyConst.USERNAME; import static com.alibaba.nacos.api.PropertyKeyConst.USERNAME;
...@@ -202,6 +203,11 @@ public class NacosConfigProperties { ...@@ -202,6 +203,11 @@ public class NacosConfigProperties {
*/ */
private String secretKey; private String secretKey;
/**
* access key for namespace.
*/
private String ramRoleName;
/** /**
* context path for nacos config server. * context path for nacos config server.
*/ */
...@@ -356,6 +362,14 @@ public class NacosConfigProperties { ...@@ -356,6 +362,14 @@ public class NacosConfigProperties {
this.secretKey = secretKey; this.secretKey = secretKey;
} }
public String getRamRoleName() {
return ramRoleName;
}
public void setRamRoleName(String ramRoleName) {
this.ramRoleName = ramRoleName;
}
public String getEncode() { public String getEncode() {
return encode; return encode;
} }
...@@ -548,6 +562,7 @@ public class NacosConfigProperties { ...@@ -548,6 +562,7 @@ public class NacosConfigProperties {
properties.put(NAMESPACE, Objects.toString(this.namespace, "")); properties.put(NAMESPACE, Objects.toString(this.namespace, ""));
properties.put(ACCESS_KEY, Objects.toString(this.accessKey, "")); properties.put(ACCESS_KEY, Objects.toString(this.accessKey, ""));
properties.put(SECRET_KEY, Objects.toString(this.secretKey, "")); properties.put(SECRET_KEY, Objects.toString(this.secretKey, ""));
properties.put(RAM_ROLE_NAME, Objects.toString(this.ramRoleName, ""));
properties.put(CLUSTER_NAME, Objects.toString(this.clusterName, "")); properties.put(CLUSTER_NAME, Objects.toString(this.clusterName, ""));
properties.put(MAX_RETRY, Objects.toString(this.maxRetry, "")); properties.put(MAX_RETRY, Objects.toString(this.maxRetry, ""));
properties.put(CONFIG_LONG_POLL_TIMEOUT, properties.put(CONFIG_LONG_POLL_TIMEOUT,
...@@ -560,8 +575,7 @@ public class NacosConfigProperties { ...@@ -560,8 +575,7 @@ public class NacosConfigProperties {
int index = endpoint.indexOf(":"); int index = endpoint.indexOf(":");
properties.put(ENDPOINT, endpoint.substring(0, index)); properties.put(ENDPOINT, endpoint.substring(0, index));
properties.put(ENDPOINT_PORT, endpoint.substring(index + 1)); properties.put(ENDPOINT_PORT, endpoint.substring(index + 1));
} } else {
else {
properties.put(ENDPOINT, endpoint); properties.put(ENDPOINT, endpoint);
} }
...@@ -597,6 +611,7 @@ public class NacosConfigProperties { ...@@ -597,6 +611,7 @@ public class NacosConfigProperties {
+ ", enableRemoteSyncConfig=" + enableRemoteSyncConfig + ", endpoint='" + ", enableRemoteSyncConfig=" + enableRemoteSyncConfig + ", endpoint='"
+ endpoint + '\'' + ", namespace='" + namespace + '\'' + ", accessKey='" + endpoint + '\'' + ", namespace='" + namespace + '\'' + ", accessKey='"
+ accessKey + '\'' + ", secretKey='" + secretKey + '\'' + accessKey + '\'' + ", secretKey='" + secretKey + '\''
+ ", ramRoleName='" + ramRoleName + '\''
+ ", contextPath='" + contextPath + '\'' + ", clusterName='" + clusterName + ", contextPath='" + contextPath + '\'' + ", clusterName='" + clusterName
+ '\'' + ", name='" + name + '\'' + '\'' + ", shares=" + sharedConfigs + '\'' + ", name='" + name + '\'' + '\'' + ", shares=" + sharedConfigs
+ ", extensions=" + extensionConfigs + ", refreshEnabled=" + ", extensions=" + extensionConfigs + ", refreshEnabled="
......
...@@ -16,12 +16,16 @@ ...@@ -16,12 +16,16 @@
package com.alibaba.cloud.nacos.client; package com.alibaba.cloud.nacos.client;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.Map; import java.util.Map;
import com.alibaba.cloud.nacos.NacosConfigProperties; import com.alibaba.cloud.nacos.NacosConfigProperties;
import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.util.CollectionUtils;
/** /**
* @author xiaojing * @author xiaojing
...@@ -58,6 +62,32 @@ public class NacosPropertySource extends MapPropertySource { ...@@ -58,6 +62,32 @@ public class NacosPropertySource extends MapPropertySource {
this.isRefreshable = isRefreshable; this.isRefreshable = isRefreshable;
} }
NacosPropertySource(List<PropertySource<?>> propertySources, String group,
String dataId, Date timestamp, boolean isRefreshable) {
this(group, dataId, getSourceMap(group, dataId, propertySources), timestamp,
isRefreshable);
}
private static Map<String, Object> getSourceMap(String group, String dataId,
List<PropertySource<?>> propertySources) {
if (CollectionUtils.isEmpty(propertySources)) {
return Collections.emptyMap();
}
// If only one, return the internal element, otherwise wrap it.
if (propertySources.size() == 1) {
PropertySource propertySource = propertySources.get(0);
if (propertySource != null && propertySource.getSource() instanceof Map) {
return (Map<String, Object>) propertySource.getSource();
}
}
// If it is multiple, it will be returned as it is, and the internal elements
// cannot be directly retrieved, so the user needs to implement the retrieval
// logic by himself
return Collections.singletonMap(
String.join(NacosConfigProperties.COMMAS, dataId, group),
propertySources);
}
public String getGroup() { public String getGroup() {
return this.group; return this.group;
} }
......
...@@ -16,9 +16,9 @@ ...@@ -16,9 +16,9 @@
package com.alibaba.cloud.nacos.client; package com.alibaba.cloud.nacos.client;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.LinkedHashMap; import java.util.List;
import java.util.Map;
import com.alibaba.cloud.nacos.NacosPropertySourceRepository; import com.alibaba.cloud.nacos.NacosPropertySourceRepository;
import com.alibaba.cloud.nacos.parser.NacosDataParserHandler; import com.alibaba.cloud.nacos.parser.NacosDataParserHandler;
...@@ -27,6 +27,7 @@ import com.alibaba.nacos.api.exception.NacosException; ...@@ -27,6 +27,7 @@ import com.alibaba.nacos.api.exception.NacosException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.core.env.PropertySource;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
...@@ -38,8 +39,6 @@ public class NacosPropertySourceBuilder { ...@@ -38,8 +39,6 @@ public class NacosPropertySourceBuilder {
private static final Logger log = LoggerFactory private static final Logger log = LoggerFactory
.getLogger(NacosPropertySourceBuilder.class); .getLogger(NacosPropertySourceBuilder.class);
private static final Map<String, Object> EMPTY_MAP = new LinkedHashMap();
private ConfigService configService; private ConfigService configService;
private long timeout; private long timeout;
...@@ -71,14 +70,15 @@ public class NacosPropertySourceBuilder { ...@@ -71,14 +70,15 @@ public class NacosPropertySourceBuilder {
*/ */
NacosPropertySource build(String dataId, String group, String fileExtension, NacosPropertySource build(String dataId, String group, String fileExtension,
boolean isRefreshable) { boolean isRefreshable) {
Map<String, Object> p = loadNacosData(dataId, group, fileExtension); List<PropertySource<?>> propertySources = loadNacosData(dataId, group,
NacosPropertySource nacosPropertySource = new NacosPropertySource(group, dataId, fileExtension);
p, new Date(), isRefreshable); NacosPropertySource nacosPropertySource = new NacosPropertySource(propertySources,
group, dataId, new Date(), isRefreshable);
NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource); NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource);
return nacosPropertySource; return nacosPropertySource;
} }
private Map<String, Object> loadNacosData(String dataId, String group, private List<PropertySource<?>> loadNacosData(String dataId, String group,
String fileExtension) { String fileExtension) {
String data = null; String data = null;
try { try {
...@@ -87,24 +87,23 @@ public class NacosPropertySourceBuilder { ...@@ -87,24 +87,23 @@ public class NacosPropertySourceBuilder {
log.warn( log.warn(
"Ignore the empty nacos configuration and get it based on dataId[{}] & group[{}]", "Ignore the empty nacos configuration and get it based on dataId[{}] & group[{}]",
dataId, group); dataId, group);
return EMPTY_MAP; return Collections.emptyList();
} }
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug(String.format( log.debug(String.format(
"Loading nacos data, dataId: '%s', group: '%s', data: %s", dataId, "Loading nacos data, dataId: '%s', group: '%s', data: %s", dataId,
group, data)); group, data));
} }
Map<String, Object> dataMap = NacosDataParserHandler.getInstance() return NacosDataParserHandler.getInstance().parseNacosData(dataId, data,
.parseNacosData(data, fileExtension); fileExtension);
return dataMap == null ? EMPTY_MAP : dataMap;
} }
catch (NacosException e) { catch (NacosException e) {
log.error("get data from Nacos error,dataId:{}, ", dataId, e); log.error("get data from Nacos error,dataId:{} ", dataId, e);
} }
catch (Exception e) { catch (Exception e) {
log.error("parse data from Nacos error,dataId:{},data:{},", dataId, data, e); log.error("parse data from Nacos error,dataId:{},data:{}", dataId, data, e);
} }
return EMPTY_MAP; return Collections.emptyList();
} }
} }
...@@ -101,7 +101,6 @@ public class NacosPropertySourceLocator implements PropertySourceLocator { ...@@ -101,7 +101,6 @@ public class NacosPropertySourceLocator implements PropertySourceLocator {
loadSharedConfiguration(composite); loadSharedConfiguration(composite);
loadExtConfiguration(composite); loadExtConfiguration(composite);
loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env); loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);
return composite; return composite;
} }
...@@ -156,16 +155,15 @@ public class NacosPropertySourceLocator implements PropertySourceLocator { ...@@ -156,16 +155,15 @@ public class NacosPropertySourceLocator implements PropertySourceLocator {
private void loadNacosConfiguration(final CompositePropertySource composite, private void loadNacosConfiguration(final CompositePropertySource composite,
List<NacosConfigProperties.Config> configs) { List<NacosConfigProperties.Config> configs) {
for (NacosConfigProperties.Config config : configs) { for (NacosConfigProperties.Config config : configs) {
String dataId = config.getDataId(); loadNacosDataIfPresent(composite, config.getDataId(), config.getGroup(),
String fileExtension = dataId.substring(dataId.lastIndexOf(DOT) + 1); NacosDataParserHandler.getInstance()
loadNacosDataIfPresent(composite, dataId, config.getGroup(), fileExtension, .getFileExtension(config.getDataId()),
config.isRefresh()); config.isRefresh());
} }
} }
private void checkConfiguration(List<NacosConfigProperties.Config> configs, private void checkConfiguration(List<NacosConfigProperties.Config> configs,
String tips) { String tips) {
String[] dataIds = new String[configs.size()];
for (int i = 0; i < configs.size(); i++) { for (int i = 0; i < configs.size(); i++) {
String dataId = configs.get(i).getDataId(); String dataId = configs.get(i).getDataId();
if (dataId == null || dataId.trim().length() == 0) { if (dataId == null || dataId.trim().length() == 0) {
...@@ -173,10 +171,7 @@ public class NacosPropertySourceLocator implements PropertySourceLocator { ...@@ -173,10 +171,7 @@ public class NacosPropertySourceLocator implements PropertySourceLocator {
"the [ spring.cloud.nacos.config.%s[%s] ] must give a dataId", "the [ spring.cloud.nacos.config.%s[%s] ] must give a dataId",
tips, i)); tips, i));
} }
dataIds[i] = dataId;
} }
// Just decide that the current dataId must have a suffix
NacosDataParserHandler.getInstance().checkDataId(dataIds);
} }
private void loadNacosDataIfPresent(final CompositePropertySource composite, private void loadNacosDataIfPresent(final CompositePropertySource composite,
......
...@@ -36,7 +36,7 @@ import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; ...@@ -36,7 +36,7 @@ import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
* *
* @author xiaojing * @author xiaojing
*/ */
@Endpoint(id = "nacos-config") @Endpoint(id = "nacosconfig")
public class NacosConfigEndpoint { public class NacosConfigEndpoint {
private final NacosConfigProperties properties; private final NacosConfigProperties properties;
......
...@@ -32,6 +32,16 @@ public class NacosConfigHealthIndicator extends AbstractHealthIndicator { ...@@ -32,6 +32,16 @@ public class NacosConfigHealthIndicator extends AbstractHealthIndicator {
private final ConfigService configService; private final ConfigService configService;
/**
* status up .
*/
private final String STATUS_UP = "UP";
/**
* status down .
*/
private final String STATUS_DOWN = "DOWN";
public NacosConfigHealthIndicator(ConfigService configService) { public NacosConfigHealthIndicator(ConfigService configService) {
this.configService = configService; this.configService = configService;
} }
...@@ -43,10 +53,10 @@ public class NacosConfigHealthIndicator extends AbstractHealthIndicator { ...@@ -43,10 +53,10 @@ public class NacosConfigHealthIndicator extends AbstractHealthIndicator {
// Set the status to Builder // Set the status to Builder
builder.status(status); builder.status(status);
switch (status) { switch (status) {
case "UP": case STATUS_UP:
builder.up(); builder.up();
break; break;
case "DOWN": case STATUS_DOWN:
builder.down(); builder.down();
break; break;
default: default:
......
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.nacos.logging;
import com.alibaba.nacos.client.logging.NacosLogging;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.GenericApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
/**
* Reload nacos log configuration file, after
* {@link org.springframework.boot.context.logging.LoggingApplicationListener}.
*
* @author mai.jh
*/
public class NacosLoggingListener implements GenericApplicationListener {
@Override
public boolean supportsEventType(ResolvableType resolvableType) {
Class<?> type = resolvableType.getRawClass();
if (type != null) {
return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(type);
}
return false;
}
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
NacosLogging.getInstance().loadConfiguration();
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 21;
}
}
...@@ -20,103 +20,87 @@ import java.io.IOException; ...@@ -20,103 +20,87 @@ import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import org.springframework.boot.env.PropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
* Nacos-specific loader, If need to support other methods of parsing,you need to do the
* following steps:
* <p>
* 1.inherit {@link AbstractPropertySourceLoader} ;<br/>
* 2. define the file{@code spring.factories} and append
* {@code org.springframework.boot.env.PropertySourceLoader=..}; <br/>
* 3.the last step validate.
* </p>
* Notice the use of {@link NacosByteArrayResource} .
*
* @author zkz * @author zkz
*/ */
public abstract class AbstractNacosDataParser { public abstract class AbstractPropertySourceLoader implements PropertySourceLoader {
protected static final String DOT = ".";
protected static final String VALUE = "value";
protected static final String EMPTY_STRING = "";
private String extension;
private AbstractNacosDataParser nextParser;
protected AbstractNacosDataParser(String extension) {
if (StringUtils.isEmpty(extension)) {
throw new IllegalArgumentException("extension cannot be empty");
}
this.extension = extension.toLowerCase();
}
/** /**
* Verify dataId extensions. * symbol: dot.
* @param extension file extension. json or xml or yml or yaml or properties
* @return valid or not
*/ */
public final boolean checkFileExtension(String extension) { static final String DOT = ".";
if (this.isLegal(extension.toLowerCase())) {
return true;
}
if (this.nextParser == null) {
return false;
}
return this.nextParser.checkFileExtension(extension);
/**
* Prevent interference with other loaders.Nacos-specific loader, unless the reload
* changes it.
* @param name the root name of the property source. If multiple documents are loaded
* an additional suffix should be added to the name for each source loaded.
* @param resource the resource to load
* @return if the resource can be loaded
*/
protected boolean canLoad(String name, Resource resource) {
return resource instanceof NacosByteArrayResource;
} }
/** /**
* Parsing nacos configuration content. * Load the resource into one or more property sources. Implementations may either
* @param data config data from Nacos * return a list containing a single source, or in the case of a multi-document format
* @param extension file extension. json or xml or yml or yaml or properties * such as yaml a source for each document in the resource.
* @return result of Properties * @param name the root name of the property source. If multiple documents are loaded
* @throws IOException thrown if there is a problem parsing config. * an additional suffix should be added to the name for each source loaded.
* @param resource the resource to load
* @return a list property sources
* @throws IOException if the source cannot be loaded
*/ */
public final Map<String, Object> parseNacosData(String data, String extension) @Override
public List<PropertySource<?>> load(String name, Resource resource)
throws IOException { throws IOException {
if (extension == null || extension.length() < 1) { if (!canLoad(name, resource)) {
throw new IllegalStateException("The file extension cannot be empty"); return Collections.emptyList();
}
if (this.isLegal(extension.toLowerCase())) {
return this.doParse(data);
} }
if (this.nextParser == null) { return this.doLoad(name, resource);
throw new IllegalStateException(getTips(extension));
}
return this.nextParser.parseNacosData(data, extension);
} }
/** /**
* Core logic for parsing. * Load the resource into one or more property sources. Implementations may either
* @param data config from Nacos * return a list containing a single source, or in the case of a multi-document format
* @return result of Properties * such as yaml a source for each document in the resource.
* @throws IOException thrown if there is a problem parsing config. * @param name the root name of the property source. If multiple documents are loaded
* an additional suffix should be added to the name for each source loaded.
* @param resource the resource to load
* @return a list property sources
* @throws IOException if the source cannot be loaded
*/ */
protected abstract Map<String, Object> doParse(String data) throws IOException; protected abstract List<PropertySource<?>> doLoad(String name, Resource resource)
throws IOException;
protected AbstractNacosDataParser setNextParser(AbstractNacosDataParser nextParser) {
this.nextParser = nextParser;
return this;
}
public AbstractNacosDataParser addNextParser(AbstractNacosDataParser nextParser) {
if (this.nextParser == null) {
this.nextParser = nextParser;
}
else {
this.nextParser.addNextParser(nextParser);
}
return this;
}
protected boolean isLegal(String extension) {
return this.extension.equalsIgnoreCase(extension)
|| this.extension.contains(extension);
}
protected void flattenedMap(Map<String, Object> result, Map<String, Object> dataMap, protected void flattenedMap(Map<String, Object> result, Map<String, Object> dataMap,
String parentKey) { String parentKey) {
Set<Map.Entry<String, Object>> entries = dataMap.entrySet(); if (dataMap == null || dataMap.isEmpty()) {
for (Iterator<Map.Entry<String, Object>> iterator = entries.iterator(); iterator return;
}
Set<Entry<String, Object>> entries = dataMap.entrySet();
for (Iterator<Entry<String, Object>> iterator = entries.iterator(); iterator
.hasNext();) { .hasNext();) {
Map.Entry<String, Object> entry = iterator.next(); Map.Entry<String, Object> entry = iterator.next();
String key = entry.getKey(); String key = entry.getKey();
...@@ -145,31 +129,4 @@ public abstract class AbstractNacosDataParser { ...@@ -145,31 +129,4 @@ public abstract class AbstractNacosDataParser {
} }
} }
/**
* Reload the key ending in `value` if need.
*/
protected Map<String, Object> reloadMap(Map<String, Object> map) {
if (map == null || map.isEmpty()) {
return null;
}
Map<String, Object> result = new LinkedHashMap<>(map);
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
if (key.contains(DOT)) {
int idx = key.lastIndexOf(DOT);
String suffix = key.substring(idx + 1);
if (VALUE.equalsIgnoreCase(suffix)) {
result.put(key.substring(0, idx), entry.getValue());
}
}
}
return result;
}
public static String getTips(String fileName) {
return String.format(
"[%s] must contains file extension with properties|yaml|yml|xml|json",
fileName);
}
} }
...@@ -16,51 +16,45 @@ ...@@ -16,51 +16,45 @@
package com.alibaba.cloud.nacos.parser; package com.alibaba.cloud.nacos.parser;
import java.io.IOException; import org.springframework.core.io.ByteArrayResource;
import java.util.LinkedHashMap;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/** /**
* Nacos-specific resource.
*
* @author zkz * @author zkz
* @author yuhuangbin
*/ */
public class NacosDataJsonParser extends AbstractNacosDataParser { public class NacosByteArrayResource extends ByteArrayResource {
protected NacosDataJsonParser() { private String filename;
super("json");
}
@Override /**
protected Map<String, Object> doParse(String data) throws IOException { * Create a new {@code ByteArrayResource}.
if (StringUtils.isEmpty(data)) { * @param byteArray the byte array to wrap
return null; */
} public NacosByteArrayResource(byte[] byteArray) {
Map<String, Object> map = parseJSON2Map(data); super(byteArray);
return this.reloadMap(map);
} }
/** /**
* JSON to Map. * Create a new {@code ByteArrayResource} with a description.
* @param json json data * @param byteArray the byte array to wrap
* @return the map convert by json string * @param description where the byte array comes from
* @throws IOException thrown if there is a problem parsing config.
*/ */
private Map<String, Object> parseJSON2Map(String json) throws IOException { public NacosByteArrayResource(byte[] byteArray, String description) {
Map<String, Object> result = new LinkedHashMap<>(32); super(byteArray, description);
}
ObjectMapper mapper = new ObjectMapper(); public void setFilename(String filename) {
Map<String, Object> nacosDataMap = mapper.readValue(json, LinkedHashMap.class); this.filename = filename;
}
if (CollectionUtils.isEmpty(nacosDataMap)) { /**
return result; * This implementation always returns {@code null}, assuming that this resource type
} * does not have a filename.
flattenedMap(result, nacosDataMap, EMPTY_STRING); */
return result; @Override
public String getFilename() {
return null == this.filename ? this.getDescription() : this.filename;
} }
} }
...@@ -17,63 +17,144 @@ ...@@ -17,63 +17,144 @@
package com.alibaba.cloud.nacos.parser; package com.alibaba.cloud.nacos.parser;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import com.alibaba.cloud.nacos.utils.NacosConfigUtils;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.boot.env.PropertiesPropertySourceLoader;
import org.springframework.boot.env.PropertySourceLoader;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import static com.alibaba.cloud.nacos.parser.AbstractPropertySourceLoader.DOT;
/** /**
* @author zkz * @author zkz
*/ */
public final class NacosDataParserHandler { public final class NacosDataParserHandler {
private AbstractNacosDataParser parser; /**
* default extension.
*/
private static final String DEFAULT_EXTENSION = "properties";
private static List<PropertySourceLoader> propertySourceLoaders;
private NacosDataParserHandler() { private NacosDataParserHandler() {
parser = this.createParser(); propertySourceLoaders = SpringFactoriesLoader
.loadFactories(PropertySourceLoader.class, getClass().getClassLoader());
} }
/** /**
* Parsing nacos configuration content. * Parsing nacos configuration content.
* @param data config from Nacos * @param configName name of nacos-config
* @param extension file extension. json or xml or yml or yaml or properties * @param configValue value from nacos-config
* @return result of LinkedHashMap * @param extension identifies the type of configValue
* @return result of Map
* @throws IOException thrown if there is a problem parsing config. * @throws IOException thrown if there is a problem parsing config.
*/ */
public Map<String, Object> parseNacosData(String data, String extension) public List<PropertySource<?>> parseNacosData(String configName, String configValue,
throws IOException { String extension) throws IOException {
if (null == parser) { if (StringUtils.isEmpty(configValue)) {
parser = this.createParser(); return Collections.emptyList();
} }
return parser.parseNacosData(data, extension); if (StringUtils.isEmpty(extension)) {
extension = this.getFileExtension(configName);
}
for (PropertySourceLoader propertySourceLoader : propertySourceLoaders) {
if (!canLoadFileExtension(propertySourceLoader, extension)) {
continue;
}
NacosByteArrayResource nacosByteArrayResource;
if (propertySourceLoader instanceof PropertiesPropertySourceLoader) {
// PropertiesPropertySourceLoader internal is to use the ISO_8859_1,
// the Chinese will be garbled, needs to transform into unicode.
nacosByteArrayResource = new NacosByteArrayResource(
NacosConfigUtils.selectiveConvertUnicode(configValue).getBytes(),
configName);
}
else {
nacosByteArrayResource = new NacosByteArrayResource(
configValue.getBytes(), configName);
}
nacosByteArrayResource.setFilename(getFileName(configName, extension));
List<PropertySource<?>> propertySourceList = propertySourceLoader
.load(configName, nacosByteArrayResource);
if (CollectionUtils.isEmpty(propertySourceList)) {
return Collections.emptyList();
}
return propertySourceList.stream().filter(Objects::nonNull)
.map(propertySource -> {
if (propertySource instanceof EnumerablePropertySource) {
String[] propertyNames = ((EnumerablePropertySource) propertySource)
.getPropertyNames();
if (propertyNames != null && propertyNames.length > 0) {
Map<String, Object> map = new LinkedHashMap<>();
Arrays.stream(propertyNames).forEach(name -> {
map.put(name, propertySource.getProperty(name));
});
return new OriginTrackedMapPropertySource(
propertySource.getName(), map, true);
}
}
return propertySource;
}).collect(Collectors.toList());
}
return Collections.emptyList();
} }
/** /**
* check the validity of file extensions in dataid. * check the current extension can be processed.
* @param dataIdAry array of dataId * @param loader the propertySourceLoader
* @return dataId handle success or not * @param extension file extension
* @return if can match extension
*/ */
public boolean checkDataId(String... dataIdAry) { private boolean canLoadFileExtension(PropertySourceLoader loader, String extension) {
StringBuilder stringBuilder = new StringBuilder(); return Arrays.stream(loader.getFileExtensions())
for (String dataId : dataIdAry) { .anyMatch((fileExtension) -> StringUtils.endsWithIgnoreCase(extension,
int idx = dataId.lastIndexOf(AbstractNacosDataParser.DOT); fileExtension));
if (idx > 0 && idx < dataId.length() - 1) { }
String extension = dataId.substring(idx + 1);
if (parser.checkFileExtension(extension)) { /**
break; * @param name filename
} * @return file extension, default {@code DEFAULT_EXTENSION} if don't get
} */
// add tips public String getFileExtension(String name) {
stringBuilder.append(dataId).append(","); if (StringUtils.isEmpty(name)) {
return DEFAULT_EXTENSION;
} }
if (stringBuilder.length() > 0) { int idx = name.lastIndexOf(DOT);
String result = stringBuilder.substring(0, stringBuilder.length() - 1); if (idx > 0 && idx < name.length() - 1) {
throw new IllegalStateException(AbstractNacosDataParser.getTips(result)); return name.substring(idx + 1);
} }
return true; return DEFAULT_EXTENSION;
} }
private AbstractNacosDataParser createParser() { private String getFileName(String name, String extension) {
return new NacosDataPropertiesParser().addNextParser(new NacosDataYamlParser()) if (StringUtils.isEmpty(extension)) {
.addNextParser(new NacosDataXmlParser()) return name;
.addNextParser(new NacosDataJsonParser()); }
if (StringUtils.isEmpty(name)) {
return extension;
}
int idx = name.lastIndexOf(DOT);
if (idx > 0 && idx < name.length() - 1) {
String ext = name.substring(idx + 1);
if (extension.equalsIgnoreCase(ext)) {
return name;
}
}
return name + DOT + extension;
} }
public static NacosDataParserHandler getInstance() { public static NacosDataParserHandler getInstance() {
......
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.nacos.parser;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
/**
* @author zkz
*/
public class NacosJsonPropertySourceLoader extends AbstractPropertySourceLoader {
/**
* constant.
*/
private static final String VALUE = "value";
/**
* Returns the file extensions that the loader supports (excluding the '.').
* @return the file extensions
*/
@Override
public String[] getFileExtensions() {
return new String[] { "json" };
}
/**
* Load the resource into one or more property sources. Implementations may either
* return a list containing a single source, or in the case of a multi-document format
* such as yaml a source for each document in the resource.
* @param name the root name of the property source. If multiple documents are loaded
* an additional suffix should be added to the name for each source loaded.
* @param resource the resource to load
* @return a list property sources
* @throws IOException if the source cannot be loaded
*/
@Override
protected List<PropertySource<?>> doLoad(String name, Resource resource)
throws IOException {
Map<String, Object> result = new LinkedHashMap<>(32);
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> nacosDataMap = mapper.readValue(resource.getInputStream(),
LinkedHashMap.class);
flattenedMap(result, nacosDataMap, null);
return Collections.singletonList(
new OriginTrackedMapPropertySource(name, this.reloadMap(result), true));
}
/**
* Reload the key ending in `value` if need.
*/
protected Map<String, Object> reloadMap(Map<String, Object> map) {
if (map == null || map.isEmpty()) {
return null;
}
Map<String, Object> result = new LinkedHashMap<>(map);
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
if (key.contains(DOT)) {
int idx = key.lastIndexOf(DOT);
String suffix = key.substring(idx + 1);
if (VALUE.equalsIgnoreCase(suffix)) {
result.put(key.substring(0, idx), entry.getValue());
}
}
}
return result;
}
}
...@@ -17,8 +17,9 @@ ...@@ -17,8 +17,9 @@
package com.alibaba.cloud.nacos.parser; package com.alibaba.cloud.nacos.parser;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
...@@ -28,39 +29,76 @@ import org.w3c.dom.Document; ...@@ -28,39 +29,76 @@ import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.boot.env.PropertiesPropertySourceLoader;
import org.springframework.core.Ordered;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
* With relatively few usage scenarios, only simple parsing is performed to reduce jar * Parsing for XML requires overwriting the default
* dependencies. * {@link PropertiesPropertySourceLoader}, because it internally rigorously validates
* ({@conde DOCTYPE}) THE XML in a way that makes it difficult to customize the
* configuration; at finally, make sure it's in the first place.
* *
* @author zkz * @author zkz
*/ */
public class NacosDataXmlParser extends AbstractNacosDataParser { public class NacosXmlPropertySourceLoader extends AbstractPropertySourceLoader
implements Ordered {
public NacosDataXmlParser() { /**
super("xml"); * Get the order value of this object.
* <p>
* Higher values are interpreted as lower priority. As a consequence, the object with
* the lowest value has the highest priority (somewhat analogous to Servlet
* {@code load-on-startup} values).
* <p>
* Same order values will result in arbitrary sort positions for the affected objects.
* @return the order value
* @see #HIGHEST_PRECEDENCE
* @see #LOWEST_PRECEDENCE
*/
@Override
public int getOrder() {
return Integer.MIN_VALUE;
} }
/**
* Returns the file extensions that the loader supports (excluding the '.').
* @return the file extensions
*/
@Override @Override
protected Map<String, Object> doParse(String data) throws IOException { public String[] getFileExtensions() {
if (StringUtils.isEmpty(data)) { return new String[] { "xml" };
return null; }
}
Map<String, Object> map = parseXml2Map(data); /**
return this.reloadMap(map); * Load the resource into one or more property sources. Implementations may either
* return a list containing a single source, or in the case of a multi-document format
* such as yaml a source for each document in the resource.
* @param name the root name of the property source. If multiple documents are loaded
* an additional suffix should be added to the name for each source loaded.
* @param resource the resource to load
* @return a list property sources
* @throws IOException if the source cannot be loaded
*/
@Override
protected List<PropertySource<?>> doLoad(String name, Resource resource)
throws IOException {
Map<String, Object> nacosDataMap = parseXml2Map(resource);
return Collections.singletonList(
new OriginTrackedMapPropertySource(name, nacosDataMap, true));
} }
private Map<String, Object> parseXml2Map(String xml) throws IOException { private Map<String, Object> parseXml2Map(Resource resource) throws IOException {
xml = xml.replaceAll("\\r", "").replaceAll("\\n", "").replaceAll("\\t", "");
Map<String, Object> map = new LinkedHashMap<>(32); Map<String, Object> map = new LinkedHashMap<>(32);
try { try {
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance() DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance()
.newDocumentBuilder(); .newDocumentBuilder();
Document document = documentBuilder Document document = documentBuilder.parse(resource.getInputStream());
.parse(new InputSource(new StringReader(xml)));
if (null == document) { if (null == document) {
return null; return null;
} }
......
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.nacos.utils;
/**
* @author zkzlx
*/
public final class NacosConfigUtils {
private NacosConfigUtils() {
}
/**
* Convert Chinese characters to Unicode.
* @param configValue value of config
* @return new string
*/
public static String selectiveConvertUnicode(String configValue) {
StringBuilder sb = new StringBuilder();
char[] chars = configValue.toCharArray();
for (char aChar : chars) {
if (isBaseLetter(aChar)) {
sb.append(aChar);
}
else {
sb.append(String.format("\\u%04x", (int) aChar));
}
}
return sb.toString();
}
/**
* char is base latin or whitespace?
* @param ch a character
* @return true or false
*/
public static boolean isBaseLetter(char ch) {
Character.UnicodeBlock ub = Character.UnicodeBlock.of(ch);
return ub == Character.UnicodeBlock.BASIC_LATIN || Character.isWhitespace(ch);
}
/**
* char is chinese?
* @param c a character
* @return true or false
*/
public static boolean isChinese(char c) {
Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
|| ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS;
}
}
...@@ -4,4 +4,9 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ ...@@ -4,4 +4,9 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.NacosConfigAutoConfiguration,\ com.alibaba.cloud.nacos.NacosConfigAutoConfiguration,\
com.alibaba.cloud.nacos.endpoint.NacosConfigEndpointAutoConfiguration com.alibaba.cloud.nacos.endpoint.NacosConfigEndpointAutoConfiguration
org.springframework.boot.diagnostics.FailureAnalyzer=\ org.springframework.boot.diagnostics.FailureAnalyzer=\
com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureAnalyzer com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureAnalyzer
\ No newline at end of file org.springframework.boot.env.PropertySourceLoader=\
com.alibaba.cloud.nacos.parser.NacosJsonPropertySourceLoader,\
com.alibaba.cloud.nacos.parser.NacosXmlPropertySourceLoader
org.springframework.context.ApplicationListener=\
com.alibaba.cloud.nacos.logging.NacosLoggingListener
\ No newline at end of file
...@@ -50,7 +50,8 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen ...@@ -50,7 +50,8 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
*/ */
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@PowerMockIgnore("javax.management.*") @PowerMockIgnore({ "javax.management.*", "javax.xml.parsers.*",
"com.sun.org.apache.xerces.internal.jaxp.*", "org.w3c.dom.*" })
@PowerMockRunnerDelegate(SpringRunner.class) @PowerMockRunnerDelegate(SpringRunner.class)
@PrepareForTest({ NacosConfigService.class }) @PrepareForTest({ NacosConfigService.class })
@SpringBootTest(classes = NacosConfigurationNoSuffixTest.TestConfig.class, properties = { @SpringBootTest(classes = NacosConfigurationNoSuffixTest.TestConfig.class, properties = {
......
...@@ -49,7 +49,8 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen ...@@ -49,7 +49,8 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
* @author zkz * @author zkz
*/ */
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@PowerMockIgnore("javax.management.*") @PowerMockIgnore({ "javax.management.*", "javax.xml.parsers.*",
"com.sun.org.apache.xerces.internal.jaxp.*", "org.w3c.dom.*" })
@PowerMockRunnerDelegate(SpringRunner.class) @PowerMockRunnerDelegate(SpringRunner.class)
@PrepareForTest({ NacosConfigService.class }) @PrepareForTest({ NacosConfigService.class })
@SpringBootTest(classes = NacosConfigurationXmlJsonTest.TestConfig.class, properties = { @SpringBootTest(classes = NacosConfigurationXmlJsonTest.TestConfig.class, properties = {
...@@ -83,13 +84,15 @@ public class NacosConfigurationXmlJsonTest { ...@@ -83,13 +84,15 @@ public class NacosConfigurationXmlJsonTest {
throws Throwable { throws Throwable {
if ("xmlApp.xml".equals(args[0]) && "test-group".equals(args[1])) { if ("xmlApp.xml".equals(args[0]) && "test-group".equals(args[1])) {
return "<top>\n" + " <first>one</first>\n" return "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<top>\n"
+ " <first>one</first>\n"
+ " <sencond value=\"two\">\n" + " <sencond value=\"two\">\n"
+ " <third>three</third>\n" + " </sencond>\n" + " <third>three</third>\n" + " </sencond>\n"
+ "</top>"; + "</top>";
} }
if ("test-name.xml".equals(args[0]) && "test-group".equals(args[1])) { if ("test-name.xml".equals(args[0]) && "test-group".equals(args[1])) {
return "<Server port=\"8005\" shutdown=\"SHUTDOWN\"> \n" return "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<Server port=\"8005\" shutdown=\"SHUTDOWN\"> \n"
+ " <Service name=\"Catalina\"> \n" + " <Service name=\"Catalina\"> \n"
+ " <Connector value=\"第二个连接器\"> \n" + " <Connector value=\"第二个连接器\"> \n"
+ " <open>开启服务</open> \n" + " <open>开启服务</open> \n"
...@@ -108,7 +111,8 @@ public class NacosConfigurationXmlJsonTest { ...@@ -108,7 +111,8 @@ public class NacosConfigurationXmlJsonTest {
if ("test-name-dev.xml".equals(args[0]) if ("test-name-dev.xml".equals(args[0])
&& "test-group".equals(args[1])) { && "test-group".equals(args[1])) {
return "<application android:label=\"@string/app_name\" android:icon=\"@drawable/osg\">\n" return "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<application android:label=\"@string/app_name\" android:icon=\"@drawable/osg\">\n"
+ " <activity android:name=\".osgViewer\"\n" + " <activity android:name=\".osgViewer\"\n"
+ " android:label=\"@string/app_name\" android:screenOrientation=\"landscape\">\n" + " android:label=\"@string/app_name\" android:screenOrientation=\"landscape\">\n"
+ " <intent-filter>\n" + " <intent-filter>\n"
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>spring-cloud-timeloit-starters</artifactId> <artifactId>spring-cloud-timeloit-starters</artifactId>
<groupId>com.timeloit.cloud</groupId> <groupId>com.timeloit.cloud</groupId>
<version>2.3.3-SNAPSHOT</version> <version>2.2.8-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
...@@ -15,6 +15,11 @@ ...@@ -15,6 +15,11 @@
<dependencies> <dependencies>
<dependency>
<groupId>com.timeloit.cloud</groupId>
<artifactId>spring-cloud-timeloit-commons</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId> <artifactId>spring-boot-actuator</artifactId>
...@@ -107,12 +112,6 @@ ...@@ -107,12 +112,6 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-test-support</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>io.projectreactor</groupId> <groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId> <artifactId>reactor-test</artifactId>
...@@ -134,4 +133,4 @@ ...@@ -134,4 +133,4 @@
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>
\ No newline at end of file
...@@ -28,9 +28,8 @@ import java.util.regex.Matcher; ...@@ -28,9 +28,8 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import com.alibaba.nacos.api.naming.NamingMaintainService; import com.alibaba.cloud.nacos.event.NacosDiscoveryInfoChangedEvent;
import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.PreservedMetadataKeys; import com.alibaba.nacos.api.naming.PreservedMetadataKeys;
import com.alibaba.nacos.client.naming.utils.UtilAndComs; import com.alibaba.nacos.client.naming.utils.UtilAndComs;
...@@ -41,14 +40,12 @@ import org.slf4j.LoggerFactory; ...@@ -41,14 +40,12 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.commons.util.InetUtils; import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import static com.alibaba.nacos.api.NacosFactory.createMaintainService;
import static com.alibaba.nacos.api.NacosFactory.createNamingService;
import static com.alibaba.nacos.api.PropertyKeyConst.ACCESS_KEY; import static com.alibaba.nacos.api.PropertyKeyConst.ACCESS_KEY;
import static com.alibaba.nacos.api.PropertyKeyConst.CLUSTER_NAME; import static com.alibaba.nacos.api.PropertyKeyConst.CLUSTER_NAME;
import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT; import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT;
...@@ -186,17 +183,17 @@ public class NacosDiscoveryProperties { ...@@ -186,17 +183,17 @@ public class NacosDiscoveryProperties {
private String secretKey; private String secretKey;
/** /**
* Heart beat interval. Time unit: second. * Heart beat interval. Time unit: millisecond.
*/ */
private Integer heartBeatInterval; private Integer heartBeatInterval;
/** /**
* Heart beat timeout. Time unit: second. * Heart beat timeout. Time unit: millisecond.
*/ */
private Integer heartBeatTimeout; private Integer heartBeatTimeout;
/** /**
* Ip delete timeout. Time unit: second. * Ip delete timeout. Time unit: millisecond.
*/ */
private Integer ipDeleteTimeout; private Integer ipDeleteTimeout;
...@@ -210,15 +207,23 @@ public class NacosDiscoveryProperties { ...@@ -210,15 +207,23 @@ public class NacosDiscoveryProperties {
*/ */
private boolean ephemeral = true; private boolean ephemeral = true;
/**
* Throw exceptions during service registration if true, otherwise, log error
* (defaults to true).
*/
private boolean failFast = true;
@Autowired @Autowired
private InetUtils inetUtils; private InetUtils inetUtils;
@Autowired @Autowired
private Environment environment; private Environment environment;
private NamingService namingService; @Autowired
private NacosServiceManager nacosServiceManager;
private NamingMaintainService namingMaintainService; @Autowired
private ApplicationEventPublisher applicationEventPublisher;
@PostConstruct @PostConstruct
public void init() throws Exception { public void init() throws Exception {
...@@ -268,15 +273,19 @@ public class NacosDiscoveryProperties { ...@@ -268,15 +273,19 @@ public class NacosDiscoveryProperties {
} }
this.overrideFromEnv(environment); this.overrideFromEnv(environment);
if (nacosServiceManager.isNacosDiscoveryInfoChanged(this)) {
Properties properties = getNacosProperties(); applicationEventPublisher
this.namingService = createNamingService(properties); .publishEvent(new NacosDiscoveryInfoChangedEvent(this));
this.namingMaintainService = createMaintainService(properties); }
} }
@PreDestroy /**
public void destroy() { * recommend to use {@link NacosServiceManager#getNamingService(Properties)}.
* @return NamingService
*/
@Deprecated
public NamingService namingServiceInstance() {
return nacosServiceManager.getNamingService(this.getNacosProperties());
} }
public String getEndpoint() { public String getEndpoint() {
...@@ -483,6 +492,51 @@ public class NacosDiscoveryProperties { ...@@ -483,6 +492,51 @@ public class NacosDiscoveryProperties {
this.ephemeral = ephemeral; this.ephemeral = ephemeral;
} }
public boolean isFailFast() {
return failFast;
}
public void setFailFast(boolean failFast) {
this.failFast = failFast;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
NacosDiscoveryProperties that = (NacosDiscoveryProperties) o;
return Objects.equals(serverAddr, that.serverAddr)
&& Objects.equals(username, that.username)
&& Objects.equals(password, that.password)
&& Objects.equals(endpoint, that.endpoint)
&& Objects.equals(namespace, that.namespace)
&& Objects.equals(logName, that.logName)
&& Objects.equals(service, that.service)
&& Objects.equals(clusterName, that.clusterName)
&& Objects.equals(group, that.group) && Objects.equals(ip, that.ip)
&& Objects.equals(port, that.port)
&& Objects.equals(networkInterface, that.networkInterface)
&& Objects.equals(accessKey, that.accessKey)
&& Objects.equals(secretKey, that.secretKey)
&& Objects.equals(heartBeatInterval, that.heartBeatInterval)
&& Objects.equals(heartBeatTimeout, that.heartBeatTimeout)
&& Objects.equals(failFast, that.failFast)
&& Objects.equals(ipDeleteTimeout, that.ipDeleteTimeout);
}
@Override
public int hashCode() {
return Objects.hash(serverAddr, username, password, endpoint, namespace,
watchDelay, logName, service, weight, clusterName, group,
namingLoadCacheAtStart, registerEnabled, ip, networkInterface, port,
secure, accessKey, secretKey, heartBeatInterval, heartBeatTimeout,
ipDeleteTimeout, instanceEnabled, ephemeral, failFast);
}
@Override @Override
public String toString() { public String toString() {
return "NacosDiscoveryProperties{" + "serverAddr='" + serverAddr + '\'' return "NacosDiscoveryProperties{" + "serverAddr='" + serverAddr + '\''
...@@ -496,7 +550,7 @@ public class NacosDiscoveryProperties { ...@@ -496,7 +550,7 @@ public class NacosDiscoveryProperties {
+ ", port=" + port + ", secure=" + secure + ", accessKey='" + accessKey + ", port=" + port + ", secure=" + secure + ", accessKey='" + accessKey
+ '\'' + ", secretKey='" + secretKey + '\'' + ", heartBeatInterval=" + '\'' + ", secretKey='" + secretKey + '\'' + ", heartBeatInterval="
+ heartBeatInterval + ", heartBeatTimeout=" + heartBeatTimeout + heartBeatInterval + ", heartBeatTimeout=" + heartBeatTimeout
+ ", ipDeleteTimeout=" + ipDeleteTimeout + '}'; + ", ipDeleteTimeout=" + ipDeleteTimeout + ", failFast=" + failFast + '}';
} }
public void overrideFromEnv(Environment env) { public void overrideFromEnv(Environment env) {
...@@ -546,16 +600,7 @@ public class NacosDiscoveryProperties { ...@@ -546,16 +600,7 @@ public class NacosDiscoveryProperties {
} }
} }
public NamingService namingServiceInstance() { public Properties getNacosProperties() {
return namingService;
}
@Deprecated
public NamingMaintainService namingMaintainServiceInstance() {
return namingMaintainService;
}
private Properties getNacosProperties() {
Properties properties = new Properties(); Properties properties = new Properties();
properties.put(SERVER_ADDR, serverAddr); properties.put(SERVER_ADDR, serverAddr);
properties.put(USERNAME, Objects.toString(username, "")); properties.put(USERNAME, Objects.toString(username, ""));
......
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.nacos;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author yuhuangbin
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
@ConditionalOnNacosDiscoveryEnabled
public class NacosServiceAutoConfiguration {
@Bean
public NacosServiceManager nacosServiceManager() {
return new NacosServiceManager();
}
}
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.nacos;
import java.util.Objects;
import java.util.Properties;
import com.alibaba.cloud.nacos.registry.NacosRegistration;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingMaintainService;
import com.alibaba.nacos.api.naming.NamingService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.discovery.event.InstancePreRegisteredEvent;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.context.event.EventListener;
import static com.alibaba.nacos.api.NacosFactory.createMaintainService;
import static com.alibaba.nacos.api.NacosFactory.createNamingService;
import static org.springframework.beans.BeanUtils.copyProperties;
/**
* @author yuhuangbin
*/
public class NacosServiceManager {
private static final Logger log = LoggerFactory.getLogger(NacosServiceManager.class);
private NacosDiscoveryProperties nacosDiscoveryPropertiesCache;
private NamingService namingService;
private NamingMaintainService namingMaintainService;
public NamingService getNamingService(Properties properties) {
if (Objects.isNull(this.namingService)) {
buildNamingService(properties);
}
return namingService;
}
public NamingMaintainService getNamingMaintainService(Properties properties) {
if (Objects.isNull(namingMaintainService)) {
buildNamingMaintainService(properties);
}
return namingMaintainService;
}
public boolean isNacosDiscoveryInfoChanged(
NacosDiscoveryProperties nacosDiscoveryProperties) {
if (Objects.isNull(nacosDiscoveryPropertiesCache)
|| this.nacosDiscoveryPropertiesCache.equals(nacosDiscoveryProperties)) {
return false;
}
copyProperties(nacosDiscoveryProperties, nacosDiscoveryPropertiesCache);
return true;
}
private NamingMaintainService buildNamingMaintainService(Properties properties) {
if (Objects.isNull(namingMaintainService)) {
synchronized (NacosServiceManager.class) {
if (Objects.isNull(namingMaintainService)) {
namingMaintainService = createNamingMaintainService(properties);
}
}
}
return namingMaintainService;
}
private NamingService buildNamingService(Properties properties) {
if (Objects.isNull(namingService)) {
synchronized (NacosServiceManager.class) {
if (Objects.isNull(namingService)) {
namingService = createNewNamingService(properties);
}
}
}
return namingService;
}
private NamingService createNewNamingService(Properties properties) {
try {
return createNamingService(properties);
}
catch (NacosException e) {
throw new RuntimeException(e);
}
}
private NamingMaintainService createNamingMaintainService(Properties properties) {
try {
return createMaintainService(properties);
}
catch (NacosException e) {
throw new RuntimeException(e);
}
}
public void nacosServiceShutDown() throws NacosException {
if (Objects.nonNull(this.namingService)) {
this.namingService.shutDown();
this.namingService = null;
}
if (Objects.nonNull(this.namingMaintainService)) {
this.namingMaintainService.shutDown();
this.namingMaintainService = null;
}
}
@EventListener
public void onInstancePreRegisteredEvent(
InstancePreRegisteredEvent instancePreRegisteredEvent) {
Registration registration = instancePreRegisteredEvent.getRegistration();
if (Objects.isNull(nacosDiscoveryPropertiesCache)
&& registration instanceof NacosRegistration) {
NacosDiscoveryProperties nacosDiscoveryProperties = ((NacosRegistration) registration)
.getNacosDiscoveryProperties();
nacosDiscoveryPropertiesCache = new NacosDiscoveryProperties();
copyProperties(nacosDiscoveryProperties, nacosDiscoveryPropertiesCache);
}
}
}
...@@ -18,6 +18,7 @@ package com.alibaba.cloud.nacos.discovery; ...@@ -18,6 +18,7 @@ package com.alibaba.cloud.nacos.discovery;
import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled; import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled; import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
...@@ -41,8 +42,9 @@ public class NacosDiscoveryAutoConfiguration { ...@@ -41,8 +42,9 @@ public class NacosDiscoveryAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public NacosServiceDiscovery nacosServiceDiscovery( public NacosServiceDiscovery nacosServiceDiscovery(
NacosDiscoveryProperties discoveryProperties) { NacosDiscoveryProperties discoveryProperties,
return new NacosServiceDiscovery(discoveryProperties); NacosServiceManager nacosServiceManager) {
return new NacosServiceDiscovery(discoveryProperties, nacosServiceManager);
} }
} }
...@@ -18,8 +18,8 @@ package com.alibaba.cloud.nacos.discovery; ...@@ -18,8 +18,8 @@ package com.alibaba.cloud.nacos.discovery;
import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled; import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
...@@ -31,7 +31,6 @@ import org.springframework.cloud.client.discovery.DiscoveryClient; ...@@ -31,7 +31,6 @@ import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration; import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
/** /**
* @author xiaojing * @author xiaojing
...@@ -56,9 +55,9 @@ public class NacosDiscoveryClientConfiguration { ...@@ -56,9 +55,9 @@ public class NacosDiscoveryClientConfiguration {
@ConditionalOnMissingBean @ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.nacos.discovery.watch.enabled", @ConditionalOnProperty(value = "spring.cloud.nacos.discovery.watch.enabled",
matchIfMissing = true) matchIfMissing = true)
public NacosWatch nacosWatch(NacosDiscoveryProperties nacosDiscoveryProperties, public NacosWatch nacosWatch(NacosServiceManager nacosServiceManager,
ObjectProvider<TaskScheduler> taskScheduler) { NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosWatch(nacosDiscoveryProperties, taskScheduler); return new NacosWatch(nacosServiceManager, nacosDiscoveryProperties);
} }
} }
...@@ -23,7 +23,9 @@ import java.util.Map; ...@@ -23,7 +23,9 @@ import java.util.Map;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceInstance; import com.alibaba.cloud.nacos.NacosServiceInstance;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.pojo.ListView;
...@@ -36,8 +38,12 @@ public class NacosServiceDiscovery { ...@@ -36,8 +38,12 @@ public class NacosServiceDiscovery {
private NacosDiscoveryProperties discoveryProperties; private NacosDiscoveryProperties discoveryProperties;
public NacosServiceDiscovery(NacosDiscoveryProperties discoveryProperties) { private NacosServiceManager nacosServiceManager;
public NacosServiceDiscovery(NacosDiscoveryProperties discoveryProperties,
NacosServiceManager nacosServiceManager) {
this.discoveryProperties = discoveryProperties; this.discoveryProperties = discoveryProperties;
this.nacosServiceManager = nacosServiceManager;
} }
/** /**
...@@ -48,8 +54,8 @@ public class NacosServiceDiscovery { ...@@ -48,8 +54,8 @@ public class NacosServiceDiscovery {
*/ */
public List<ServiceInstance> getInstances(String serviceId) throws NacosException { public List<ServiceInstance> getInstances(String serviceId) throws NacosException {
String group = discoveryProperties.getGroup(); String group = discoveryProperties.getGroup();
List<Instance> instances = discoveryProperties.namingServiceInstance() List<Instance> instances = namingService().selectInstances(serviceId, group,
.selectInstances(serviceId, group, true); true);
return hostToServiceInstanceList(instances, serviceId); return hostToServiceInstanceList(instances, serviceId);
} }
...@@ -60,8 +66,8 @@ public class NacosServiceDiscovery { ...@@ -60,8 +66,8 @@ public class NacosServiceDiscovery {
*/ */
public List<String> getServices() throws NacosException { public List<String> getServices() throws NacosException {
String group = discoveryProperties.getGroup(); String group = discoveryProperties.getGroup();
ListView<String> services = discoveryProperties.namingServiceInstance() ListView<String> services = namingService().getServicesOfServer(1,
.getServicesOfServer(1, Integer.MAX_VALUE, group); Integer.MAX_VALUE, group);
return services.getData(); return services.getData();
} }
...@@ -105,4 +111,9 @@ public class NacosServiceDiscovery { ...@@ -105,4 +111,9 @@ public class NacosServiceDiscovery {
return nacosServiceInstance; return nacosServiceInstance;
} }
private NamingService namingService() {
return nacosServiceManager
.getNamingService(discoveryProperties.getNacosProperties());
}
} }
...@@ -16,60 +16,73 @@ ...@@ -16,60 +16,73 @@
package com.alibaba.cloud.nacos.discovery; package com.alibaba.cloud.nacos.discovery;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.Event;
import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent; import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.SmartLifecycle; import org.springframework.context.SmartLifecycle;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
/** /**
* @author xiaojing * @author xiaojing
* @author yuhuangbin
* @author pengfei.lu
*/ */
public class NacosWatch implements ApplicationEventPublisherAware, SmartLifecycle { public class NacosWatch implements ApplicationEventPublisherAware, SmartLifecycle, DisposableBean {
private static final Logger log = LoggerFactory.getLogger(NacosWatch.class); private static final Logger log = LoggerFactory.getLogger(NacosWatch.class);
private final NacosDiscoveryProperties properties; private Map<String, EventListener> listenerMap = new ConcurrentHashMap<>(16);
private final TaskScheduler taskScheduler; private final AtomicBoolean running = new AtomicBoolean(false);
private final AtomicLong nacosWatchIndex = new AtomicLong(0); private final AtomicLong nacosWatchIndex = new AtomicLong(0);
private final AtomicBoolean running = new AtomicBoolean(false);
private ApplicationEventPublisher publisher; private ApplicationEventPublisher publisher;
private ScheduledFuture<?> watchFuture; private ScheduledFuture<?> watchFuture;
public NacosWatch(NacosDiscoveryProperties properties) { private NacosServiceManager nacosServiceManager;
this(properties, getTaskScheduler());
}
public NacosWatch(NacosDiscoveryProperties properties, TaskScheduler taskScheduler) { private final NacosDiscoveryProperties properties;
private final ThreadPoolTaskScheduler taskScheduler;
public NacosWatch(NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties properties) {
this.nacosServiceManager = nacosServiceManager;
this.properties = properties; this.properties = properties;
this.taskScheduler = taskScheduler; this.taskScheduler = getTaskScheduler();
} }
/** @Deprecated
* The constructor with {@link NacosDiscoveryProperties} bean and the optional. public NacosWatch(NacosServiceManager nacosServiceManager,
* {@link TaskScheduler} bean NacosDiscoveryProperties properties,
* @param properties {@link NacosDiscoveryProperties} bean ObjectProvider<ThreadPoolTaskScheduler> taskScheduler) {
* @param taskScheduler the optional {@link TaskScheduler} bean this.nacosServiceManager = nacosServiceManager;
* @since 2.2.0 this.properties = properties;
*/ this.taskScheduler = taskScheduler.stream().findAny()
public NacosWatch(NacosDiscoveryProperties properties, .orElseGet(NacosWatch::getTaskScheduler);
ObjectProvider<TaskScheduler> taskScheduler) {
this(properties, taskScheduler.getIfAvailable(NacosWatch::getTaskScheduler));
} }
private static ThreadPoolTaskScheduler getTaskScheduler() { private static ThreadPoolTaskScheduler getTaskScheduler() {
...@@ -98,19 +111,75 @@ public class NacosWatch implements ApplicationEventPublisherAware, SmartLifecycl ...@@ -98,19 +111,75 @@ public class NacosWatch implements ApplicationEventPublisherAware, SmartLifecycl
@Override @Override
public void start() { public void start() {
if (this.running.compareAndSet(false, true)) { if (this.running.compareAndSet(false, true)) {
EventListener eventListener = listenerMap.computeIfAbsent(buildKey(),
event -> new EventListener() {
@Override
public void onEvent(Event event) {
if (event instanceof NamingEvent) {
List<Instance> instances = ((NamingEvent) event)
.getInstances();
Optional<Instance> instanceOptional = selectCurrentInstance(
instances);
instanceOptional.ifPresent(currentInstance -> {
resetIfNeeded(currentInstance);
});
}
}
});
NamingService namingService = nacosServiceManager
.getNamingService(properties.getNacosProperties());
try {
namingService.subscribe(properties.getService(), properties.getGroup(),
Arrays.asList(properties.getClusterName()), eventListener);
}
catch (Exception e) {
log.error("namingService subscribe failed, properties:{}", properties, e);
}
this.watchFuture = this.taskScheduler.scheduleWithFixedDelay( this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(
this::nacosServicesWatch, this.properties.getWatchDelay()); this::nacosServicesWatch, this.properties.getWatchDelay());
} }
} }
private String buildKey() {
return String.join(":", properties.getService(), properties.getGroup());
}
private void resetIfNeeded(Instance instance) {
if (!properties.getMetadata().equals(instance.getMetadata())) {
properties.setMetadata(instance.getMetadata());
}
}
private Optional<Instance> selectCurrentInstance(List<Instance> instances) {
return instances.stream()
.filter(instance -> properties.getIp().equals(instance.getIp())
&& properties.getPort() == instance.getPort())
.findFirst();
}
@Override @Override
public void stop() { public void stop() {
if (this.running.compareAndSet(true, false) && this.watchFuture != null) { if (this.running.compareAndSet(true, false)) {
// shutdown current user-thread, if (this.watchFuture != null) {
// then the other daemon-threads will terminate automatic. // shutdown current user-thread,
((ThreadPoolTaskScheduler) this.taskScheduler).shutdown(); // then the other daemon-threads will terminate automatic.
this.taskScheduler.shutdown();
this.watchFuture.cancel(true); this.watchFuture.cancel(true);
}
EventListener eventListener = listenerMap.get(buildKey());
try {
NamingService namingService = nacosServiceManager
.getNamingService(properties.getNacosProperties());
namingService.unsubscribe(properties.getService(), properties.getGroup(),
Arrays.asList(properties.getClusterName()), eventListener);
}
catch (Exception e) {
log.error("namingService unsubscribe failed, properties:{}", properties,
e);
}
} }
} }
...@@ -132,4 +201,8 @@ public class NacosWatch implements ApplicationEventPublisherAware, SmartLifecycl ...@@ -132,4 +201,8 @@ public class NacosWatch implements ApplicationEventPublisherAware, SmartLifecycl
} }
@Override
public void destroy() {
this.stop();
}
} }
...@@ -31,6 +31,16 @@ import org.springframework.boot.actuate.health.HealthIndicator; ...@@ -31,6 +31,16 @@ import org.springframework.boot.actuate.health.HealthIndicator;
*/ */
public class NacosDiscoveryHealthIndicator extends AbstractHealthIndicator { public class NacosDiscoveryHealthIndicator extends AbstractHealthIndicator {
/**
* status up.
*/
private static final String STATUS_UP = "UP";
/**
* status down.
*/
private static final String STATUS_DOWN = "DOWN";
private final NamingService namingService; private final NamingService namingService;
public NacosDiscoveryHealthIndicator(NamingService namingService) { public NacosDiscoveryHealthIndicator(NamingService namingService) {
...@@ -44,10 +54,10 @@ public class NacosDiscoveryHealthIndicator extends AbstractHealthIndicator { ...@@ -44,10 +54,10 @@ public class NacosDiscoveryHealthIndicator extends AbstractHealthIndicator {
// Set the status to Builder // Set the status to Builder
builder.status(status); builder.status(status);
switch (status) { switch (status) {
case "UP": case STATUS_UP:
builder.up(); builder.up();
break; break;
case "DOWN": case STATUS_DOWN:
builder.down(); builder.down();
break; break;
default: default:
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.alibaba.cloud.nacos.discovery.configclient; package com.alibaba.cloud.nacos.discovery.configclient;
import com.alibaba.cloud.nacos.NacosServiceAutoConfiguration;
import com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration; import com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration;
import com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration; import com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration;
import com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration; import com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration;
...@@ -36,7 +37,7 @@ import org.springframework.context.annotation.Configuration; ...@@ -36,7 +37,7 @@ import org.springframework.context.annotation.Configuration;
matchIfMissing = false) matchIfMissing = false)
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ImportAutoConfiguration({ NacosDiscoveryAutoConfiguration.class, @ImportAutoConfiguration({ NacosDiscoveryAutoConfiguration.class,
NacosDiscoveryClientConfiguration.class, NacosServiceAutoConfiguration.class, NacosDiscoveryClientConfiguration.class,
NacosReactiveDiscoveryClientConfiguration.class }) NacosReactiveDiscoveryClientConfiguration.class })
public class NacosDiscoveryClientConfigServiceBootstrapConfiguration { public class NacosDiscoveryClientConfigServiceBootstrapConfiguration {
......
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.nacos.discovery.logging;
import com.alibaba.nacos.client.logging.NacosLogging;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.GenericApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
/**
* Reload nacos log configuration file, after
* {@link org.springframework.boot.context.logging.LoggingApplicationListener}.
*
* @author mai.jh
*/
public class NacosLoggingListener implements GenericApplicationListener {
@Override
public boolean supportsEventType(ResolvableType resolvableType) {
Class<?> type = resolvableType.getRawClass();
if (type != null) {
return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(type);
}
return false;
}
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
NacosLogging.getInstance().loadConfiguration();
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 21;
}
}
...@@ -22,7 +22,9 @@ import java.util.List; ...@@ -22,7 +22,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -35,15 +37,19 @@ import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; ...@@ -35,15 +37,19 @@ import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
* *
* @author xiaojing * @author xiaojing
*/ */
@Endpoint(id = "nacos-discovery") @Endpoint(id = "nacosdiscovery")
public class NacosDiscoveryEndpoint { public class NacosDiscoveryEndpoint {
private static final Logger log = LoggerFactory private static final Logger log = LoggerFactory
.getLogger(NacosDiscoveryEndpoint.class); .getLogger(NacosDiscoveryEndpoint.class);
private NacosServiceManager nacosServiceManager;
private NacosDiscoveryProperties nacosDiscoveryProperties; private NacosDiscoveryProperties nacosDiscoveryProperties;
public NacosDiscoveryEndpoint(NacosDiscoveryProperties nacosDiscoveryProperties) { public NacosDiscoveryEndpoint(NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties) {
this.nacosServiceManager = nacosServiceManager;
this.nacosDiscoveryProperties = nacosDiscoveryProperties; this.nacosDiscoveryProperties = nacosDiscoveryProperties;
} }
...@@ -55,11 +61,17 @@ public class NacosDiscoveryEndpoint { ...@@ -55,11 +61,17 @@ public class NacosDiscoveryEndpoint {
Map<String, Object> result = new HashMap<>(); Map<String, Object> result = new HashMap<>();
result.put("NacosDiscoveryProperties", nacosDiscoveryProperties); result.put("NacosDiscoveryProperties", nacosDiscoveryProperties);
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance(); NamingService namingService = nacosServiceManager
.getNamingService(nacosDiscoveryProperties.getNacosProperties());
List<ServiceInfo> subscribe = Collections.emptyList(); List<ServiceInfo> subscribe = Collections.emptyList();
try { try {
subscribe = namingService.getSubscribeServices(); subscribe = namingService.getSubscribeServices();
for (ServiceInfo serviceInfo : subscribe) {
List<Instance> instances = namingService.getAllInstances(
serviceInfo.getName(), serviceInfo.getGroupName());
serviceInfo.setHosts(instances);
}
} }
catch (Exception e) { catch (Exception e) {
log.error("get subscribe services from nacos fail,", e); log.error("get subscribe services from nacos fail,", e);
......
...@@ -16,8 +16,11 @@ ...@@ -16,8 +16,11 @@
package com.alibaba.cloud.nacos.endpoint; package com.alibaba.cloud.nacos.endpoint;
import java.util.Properties;
import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled; import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.discovery.actuate.health.NacosDiscoveryHealthIndicator; import com.alibaba.cloud.nacos.discovery.actuate.health.NacosDiscoveryHealthIndicator;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
...@@ -26,7 +29,6 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint; ...@@ -26,7 +29,6 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -44,18 +46,20 @@ public class NacosDiscoveryEndpointAutoConfiguration { ...@@ -44,18 +46,20 @@ public class NacosDiscoveryEndpointAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
@ConditionalOnAvailableEndpoint @ConditionalOnAvailableEndpoint
public NacosDiscoveryEndpoint nacosDiscoveryEndpoint( public NacosDiscoveryEndpoint nacosDiscoveryEndpoint(
NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties) { NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosDiscoveryEndpoint(nacosDiscoveryProperties); return new NacosDiscoveryEndpoint(nacosServiceManager, nacosDiscoveryProperties);
} }
@Bean @Bean
@ConditionalOnEnabledHealthIndicator("nacos-discovery") @ConditionalOnEnabledHealthIndicator("nacos-discovery")
public HealthIndicator nacosDiscoveryHealthIndicator( public HealthIndicator nacosDiscoveryHealthIndicator(
NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties) { NacosDiscoveryProperties nacosDiscoveryProperties) {
Properties nacosProperties = nacosDiscoveryProperties.getNacosProperties();
return new NacosDiscoveryHealthIndicator( return new NacosDiscoveryHealthIndicator(
nacosDiscoveryProperties.namingServiceInstance()); nacosServiceManager.getNamingService(nacosProperties));
} }
} }
...@@ -14,31 +14,25 @@ ...@@ -14,31 +14,25 @@
* limitations under the License. * limitations under the License.
*/ */
package com.alibaba.cloud.nacos.parser; package com.alibaba.cloud.nacos.event;
import java.util.LinkedHashMap; import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import java.util.Map;
import org.springframework.beans.factory.config.YamlMapFactoryBean; import org.springframework.context.ApplicationEvent;
import org.springframework.core.io.ByteArrayResource;
/** /**
* @author zkz * @author yuhuangbin
*/ */
public class NacosDataYamlParser extends AbstractNacosDataParser { public class NacosDiscoveryInfoChangedEvent extends ApplicationEvent {
public NacosDataYamlParser() { public NacosDiscoveryInfoChangedEvent(
super(",yml,yaml,"); NacosDiscoveryProperties nacosDiscoveryProperties) {
super(nacosDiscoveryProperties);
} }
@Override @Override
protected Map<String, Object> doParse(String data) { public NacosDiscoveryProperties getSource() {
YamlMapFactoryBean yamlFactory = new YamlMapFactoryBean(); return (NacosDiscoveryProperties) super.getSource();
yamlFactory.setResources(new ByteArrayResource(data.getBytes()));
Map<String, Object> result = new LinkedHashMap<>();
flattenedMap(result, yamlFactory.getObject(), EMPTY_STRING);
return result;
} }
} }
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.alibaba.cloud.nacos.registry; package com.alibaba.cloud.nacos.registry;
import com.alibaba.cloud.nacos.event.NacosDiscoveryInfoChangedEvent;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -23,6 +24,7 @@ import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegis ...@@ -23,6 +24,7 @@ import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegis
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry; import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.event.EventListener;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
...@@ -102,4 +104,14 @@ public class NacosAutoServiceRegistration ...@@ -102,4 +104,14 @@ public class NacosAutoServiceRegistration
return StringUtils.isEmpty(appName) ? super.getAppName() : appName; return StringUtils.isEmpty(appName) ? super.getAppName() : appName;
} }
@EventListener
public void onNacosDiscoveryInfoChangedEvent(NacosDiscoveryInfoChangedEvent event) {
restart();
}
private void restart() {
this.stop();
this.start();
}
} }
...@@ -17,12 +17,12 @@ ...@@ -17,12 +17,12 @@
package com.alibaba.cloud.nacos.registry; package com.alibaba.cloud.nacos.registry;
import java.net.URI; import java.net.URI;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.PreservedMetadataKeys; import com.alibaba.nacos.api.naming.PreservedMetadataKeys;
import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.client.DefaultServiceInstance;
...@@ -58,12 +58,16 @@ public class NacosRegistration implements Registration, ServiceInstance { ...@@ -58,12 +58,16 @@ public class NacosRegistration implements Registration, ServiceInstance {
*/ */
public static final String MANAGEMENT_ENDPOINT_BASE_PATH = "management.endpoints.web.base-path"; public static final String MANAGEMENT_ENDPOINT_BASE_PATH = "management.endpoints.web.base-path";
private List<NacosRegistrationCustomizer> registrationCustomizers;
private NacosDiscoveryProperties nacosDiscoveryProperties; private NacosDiscoveryProperties nacosDiscoveryProperties;
private ApplicationContext context; private ApplicationContext context;
public NacosRegistration(NacosDiscoveryProperties nacosDiscoveryProperties, public NacosRegistration(List<NacosRegistrationCustomizer> registrationCustomizers,
NacosDiscoveryProperties nacosDiscoveryProperties,
ApplicationContext context) { ApplicationContext context) {
this.registrationCustomizers = registrationCustomizers;
this.nacosDiscoveryProperties = nacosDiscoveryProperties; this.nacosDiscoveryProperties = nacosDiscoveryProperties;
this.context = context; this.context = context;
} }
...@@ -105,6 +109,17 @@ public class NacosRegistration implements Registration, ServiceInstance { ...@@ -105,6 +109,17 @@ public class NacosRegistration implements Registration, ServiceInstance {
metadata.put(PreservedMetadataKeys.IP_DELETE_TIMEOUT, metadata.put(PreservedMetadataKeys.IP_DELETE_TIMEOUT,
nacosDiscoveryProperties.getIpDeleteTimeout().toString()); nacosDiscoveryProperties.getIpDeleteTimeout().toString());
} }
customize(registrationCustomizers, this);
}
private static void customize(
List<NacosRegistrationCustomizer> registrationCustomizers,
NacosRegistration registration) {
if (registrationCustomizers != null) {
for (NacosRegistrationCustomizer customizer : registrationCustomizers) {
customizer.customize(registration);
}
}
} }
@Override @Override
...@@ -157,10 +172,6 @@ public class NacosRegistration implements Registration, ServiceInstance { ...@@ -157,10 +172,6 @@ public class NacosRegistration implements Registration, ServiceInstance {
return nacosDiscoveryProperties; return nacosDiscoveryProperties;
} }
public NamingService getNacosNamingService() {
return nacosDiscoveryProperties.namingServiceInstance();
}
@Override @Override
public String toString() { public String toString() {
return "NacosRegistration{" + "nacosDiscoveryProperties=" return "NacosRegistration{" + "nacosDiscoveryProperties="
......
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.nacos.registry;
/**
* @author L.cm
*/
public interface NacosRegistrationCustomizer {
/**
* customize NacosRegistration.
* @param registration NacosRegistration
*/
void customize(NacosRegistration registration);
}
...@@ -17,8 +17,11 @@ ...@@ -17,8 +17,11 @@
package com.alibaba.cloud.nacos.registry; package com.alibaba.cloud.nacos.registry;
import java.util.List; import java.util.List;
import java.util.Properties;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.Instance;
import org.slf4j.Logger; import org.slf4j.Logger;
...@@ -37,12 +40,20 @@ import static org.springframework.util.ReflectionUtils.rethrowRuntimeException; ...@@ -37,12 +40,20 @@ import static org.springframework.util.ReflectionUtils.rethrowRuntimeException;
*/ */
public class NacosServiceRegistry implements ServiceRegistry<Registration> { public class NacosServiceRegistry implements ServiceRegistry<Registration> {
private static final String STATUS_UP = "UP";
private static final String STATUS_DOWN = "DOWN";
private static final Logger log = LoggerFactory.getLogger(NacosServiceRegistry.class); private static final Logger log = LoggerFactory.getLogger(NacosServiceRegistry.class);
private final NacosDiscoveryProperties nacosDiscoveryProperties; private final NacosDiscoveryProperties nacosDiscoveryProperties;
public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) { private final NacosServiceManager nacosServiceManager;
public NacosServiceRegistry(NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties) {
this.nacosDiscoveryProperties = nacosDiscoveryProperties; this.nacosDiscoveryProperties = nacosDiscoveryProperties;
this.nacosServiceManager = nacosServiceManager;
} }
@Override @Override
...@@ -65,11 +76,15 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> { ...@@ -65,11 +76,15 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> {
instance.getIp(), instance.getPort()); instance.getIp(), instance.getPort());
} }
catch (Exception e) { catch (Exception e) {
log.error("nacos registry, {} register failed...{},", serviceId, if (nacosDiscoveryProperties.isFailFast()) {
registration.toString(), e); log.error("nacos registry, {} register failed...{},", serviceId,
// rethrow a RuntimeException if the registration is failed. registration.toString(), e);
// issue : https://github.com/alibaba/spring-cloud-alibaba/issues/1132 rethrowRuntimeException(e);
rethrowRuntimeException(e); }
else {
log.warn("Failfast is false. {} register failed...{},", serviceId,
registration.toString(), e);
}
} }
} }
...@@ -101,13 +116,19 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> { ...@@ -101,13 +116,19 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> {
@Override @Override
public void close() { public void close() {
try {
nacosServiceManager.nacosServiceShutDown();
}
catch (NacosException e) {
log.error("Nacos namingService shutDown failed", e);
}
} }
@Override @Override
public void setStatus(Registration registration, String status) { public void setStatus(Registration registration, String status) {
if (!status.equalsIgnoreCase("UP") && !status.equalsIgnoreCase("DOWN")) { if (!STATUS_UP.equalsIgnoreCase(status)
&& !STATUS_DOWN.equalsIgnoreCase(status)) {
log.warn("can't support status {},please choose UP or DOWN", status); log.warn("can't support status {},please choose UP or DOWN", status);
return; return;
} }
...@@ -116,7 +137,7 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> { ...@@ -116,7 +137,7 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> {
Instance instance = getNacosInstanceFromRegistration(registration); Instance instance = getNacosInstanceFromRegistration(registration);
if (status.equalsIgnoreCase("DOWN")) { if (STATUS_DOWN.equalsIgnoreCase(status)) {
instance.setEnabled(false); instance.setEnabled(false);
} }
else { else {
...@@ -124,8 +145,9 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> { ...@@ -124,8 +145,9 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> {
} }
try { try {
nacosDiscoveryProperties.namingMaintainServiceInstance() Properties nacosProperties = nacosDiscoveryProperties.getNacosProperties();
.updateInstance(serviceId, instance); nacosServiceManager.getNamingMaintainService(nacosProperties).updateInstance(
serviceId, nacosDiscoveryProperties.getGroup(), instance);
} }
catch (Exception e) { catch (Exception e) {
throw new RuntimeException("update nacos instance status fail", e); throw new RuntimeException("update nacos instance status fail", e);
...@@ -137,9 +159,10 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> { ...@@ -137,9 +159,10 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> {
public Object getStatus(Registration registration) { public Object getStatus(Registration registration) {
String serviceName = registration.getServiceId(); String serviceName = registration.getServiceId();
String group = nacosDiscoveryProperties.getGroup();
try { try {
List<Instance> instances = nacosDiscoveryProperties.namingServiceInstance() List<Instance> instances = namingService().getAllInstances(serviceName,
.getAllInstances(serviceName); group);
for (Instance instance : instances) { for (Instance instance : instances) {
if (instance.getIp().equalsIgnoreCase(nacosDiscoveryProperties.getIp()) if (instance.getIp().equalsIgnoreCase(nacosDiscoveryProperties.getIp())
&& instance.getPort() == nacosDiscoveryProperties.getPort()) { && instance.getPort() == nacosDiscoveryProperties.getPort()) {
...@@ -166,7 +189,8 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> { ...@@ -166,7 +189,8 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> {
} }
private NamingService namingService() { private NamingService namingService() {
return nacosDiscoveryProperties.namingServiceInstance(); return nacosServiceManager
.getNamingService(nacosDiscoveryProperties.getNacosProperties());
} }
} }
...@@ -16,10 +16,14 @@ ...@@ -16,10 +16,14 @@
package com.alibaba.cloud.nacos.registry; package com.alibaba.cloud.nacos.registry;
import java.util.List;
import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled; import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration; import com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
...@@ -47,16 +51,19 @@ public class NacosServiceRegistryAutoConfiguration { ...@@ -47,16 +51,19 @@ public class NacosServiceRegistryAutoConfiguration {
@Bean @Bean
public NacosServiceRegistry nacosServiceRegistry( public NacosServiceRegistry nacosServiceRegistry(
NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties) { NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosServiceRegistry(nacosDiscoveryProperties); return new NacosServiceRegistry(nacosServiceManager, nacosDiscoveryProperties);
} }
@Bean @Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class) @ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosRegistration nacosRegistration( public NacosRegistration nacosRegistration(
ObjectProvider<List<NacosRegistrationCustomizer>> registrationCustomizers,
NacosDiscoveryProperties nacosDiscoveryProperties, NacosDiscoveryProperties nacosDiscoveryProperties,
ApplicationContext context) { ApplicationContext context) {
return new NacosRegistration(nacosDiscoveryProperties, context); return new NacosRegistration(registrationCustomizers.getIfAvailable(),
nacosDiscoveryProperties, context);
} }
@Bean @Bean
......
...@@ -20,14 +20,15 @@ import java.util.List; ...@@ -20,14 +20,15 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig; import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.DynamicServerListLoadBalancer; import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.Server;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -47,6 +48,9 @@ public class NacosRule extends AbstractLoadBalancerRule { ...@@ -47,6 +48,9 @@ public class NacosRule extends AbstractLoadBalancerRule {
@Autowired @Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties; private NacosDiscoveryProperties nacosDiscoveryProperties;
@Autowired
private NacosServiceManager nacosServiceManager;
@Override @Override
public Server choose(Object key) { public Server choose(Object key) {
try { try {
...@@ -55,8 +59,8 @@ public class NacosRule extends AbstractLoadBalancerRule { ...@@ -55,8 +59,8 @@ public class NacosRule extends AbstractLoadBalancerRule {
DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer(); DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
String name = loadBalancer.getName(); String name = loadBalancer.getName();
NamingService namingService = nacosDiscoveryProperties NamingService namingService = nacosServiceManager
.namingServiceInstance(); .getNamingService(nacosDiscoveryProperties.getNacosProperties());
List<Instance> instances = namingService.selectInstances(name, group, true); List<Instance> instances = namingService.selectInstances(name, group, true);
if (CollectionUtils.isEmpty(instances)) { if (CollectionUtils.isEmpty(instances)) {
LOGGER.warn("no instance in service {}", name); LOGGER.warn("no instance in service {}", name);
......
...@@ -5,6 +5,9 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ ...@@ -5,6 +5,9 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration,\ com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration,\
com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration,\ com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration,\
com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration,\ com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration,\
com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration,\
com.alibaba.cloud.nacos.NacosServiceAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\ org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration
org.springframework.context.ApplicationListener=\
com.alibaba.cloud.nacos.discovery.logging.NacosLoggingListener
...@@ -22,6 +22,7 @@ import java.util.LinkedList; ...@@ -22,6 +22,7 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.Instance;
...@@ -60,16 +61,19 @@ public class NacosServiceDiscoveryTest { ...@@ -60,16 +61,19 @@ public class NacosServiceDiscoveryTest {
NacosDiscoveryProperties nacosDiscoveryProperties = mock( NacosDiscoveryProperties nacosDiscoveryProperties = mock(
NacosDiscoveryProperties.class); NacosDiscoveryProperties.class);
NacosServiceManager nacosServiceManager = mock(NacosServiceManager.class);
NamingService namingService = mock(NamingService.class); NamingService namingService = mock(NamingService.class);
when(nacosDiscoveryProperties.namingServiceInstance()).thenReturn(namingService); when(nacosServiceManager
.getNamingService(nacosDiscoveryProperties.getNacosProperties()))
.thenReturn(namingService);
when(nacosDiscoveryProperties.getGroup()).thenReturn("DEFAULT"); when(nacosDiscoveryProperties.getGroup()).thenReturn("DEFAULT");
when(namingService.selectInstances(eq(serviceName), eq("DEFAULT"), eq(true))) when(namingService.selectInstances(eq(serviceName), eq("DEFAULT"), eq(true)))
.thenReturn(instances); .thenReturn(instances);
NacosServiceDiscovery serviceDiscovery = new NacosServiceDiscovery( NacosServiceDiscovery serviceDiscovery = new NacosServiceDiscovery(
nacosDiscoveryProperties); nacosDiscoveryProperties, nacosServiceManager);
List<ServiceInstance> serviceInstances = serviceDiscovery List<ServiceInstance> serviceInstances = serviceDiscovery
.getInstances(serviceName); .getInstances(serviceName);
...@@ -99,16 +103,19 @@ public class NacosServiceDiscoveryTest { ...@@ -99,16 +103,19 @@ public class NacosServiceDiscoveryTest {
NacosDiscoveryProperties nacosDiscoveryProperties = mock( NacosDiscoveryProperties nacosDiscoveryProperties = mock(
NacosDiscoveryProperties.class); NacosDiscoveryProperties.class);
NacosServiceManager nacosServiceManager = mock(NacosServiceManager.class);
NamingService namingService = mock(NamingService.class); NamingService namingService = mock(NamingService.class);
when(nacosDiscoveryProperties.namingServiceInstance()).thenReturn(namingService); when(nacosServiceManager
.getNamingService(nacosDiscoveryProperties.getNacosProperties()))
.thenReturn(namingService);
when(nacosDiscoveryProperties.getGroup()).thenReturn("DEFAULT"); when(nacosDiscoveryProperties.getGroup()).thenReturn("DEFAULT");
when(namingService.getServicesOfServer(eq(1), eq(Integer.MAX_VALUE), when(namingService.getServicesOfServer(eq(1), eq(Integer.MAX_VALUE),
eq("DEFAULT"))).thenReturn(nacosServices); eq("DEFAULT"))).thenReturn(nacosServices);
NacosServiceDiscovery serviceDiscovery = new NacosServiceDiscovery( NacosServiceDiscovery serviceDiscovery = new NacosServiceDiscovery(
nacosDiscoveryProperties); nacosDiscoveryProperties, nacosServiceManager);
List<String> services = serviceDiscovery.getServices(); List<String> services = serviceDiscovery.getServices();
......
...@@ -22,6 +22,7 @@ import java.util.Map; ...@@ -22,6 +22,7 @@ import java.util.Map;
import java.util.Properties; import java.util.Properties;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration; import com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration;
import com.alibaba.cloud.nacos.endpoint.NacosDiscoveryEndpoint; import com.alibaba.cloud.nacos.endpoint.NacosDiscoveryEndpoint;
import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.NacosFactory;
...@@ -68,9 +69,9 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen ...@@ -68,9 +69,9 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
"spring.cloud.nacos.discovery.accessKey=test-accessKey", "spring.cloud.nacos.discovery.accessKey=test-accessKey",
"spring.cloud.nacos.discovery.ip=8.8.8.8", "spring.cloud.nacos.discovery.ip=8.8.8.8",
"spring.cloud.nacos.discovery.secretKey=test-secretKey", "spring.cloud.nacos.discovery.secretKey=test-secretKey",
"spring.cloud.nacos.discovery.heart-beat-interval=3", "spring.cloud.nacos.discovery.heart-beat-interval=3000",
"spring.cloud.nacos.discovery.heart-beat-timeout=6", "spring.cloud.nacos.discovery.heart-beat-timeout=6000",
"spring.cloud.nacos.discovery.ip-delete-timeout=9" }, "spring.cloud.nacos.discovery.ip-delete-timeout=9000" },
webEnvironment = RANDOM_PORT) webEnvironment = RANDOM_PORT)
public class NacosAutoServiceRegistrationTests { public class NacosAutoServiceRegistrationTests {
...@@ -86,6 +87,9 @@ public class NacosAutoServiceRegistrationTests { ...@@ -86,6 +87,9 @@ public class NacosAutoServiceRegistrationTests {
@Autowired @Autowired
private NacosDiscoveryProperties properties; private NacosDiscoveryProperties properties;
@Autowired
private NacosServiceManager nacosServiceManager;
@Autowired @Autowired
private InetUtils inetUtils; private InetUtils inetUtils;
...@@ -182,15 +186,15 @@ public class NacosAutoServiceRegistrationTests { ...@@ -182,15 +186,15 @@ public class NacosAutoServiceRegistrationTests {
} }
private void checkoutNacosDiscoveryHeartBeatInterval() { private void checkoutNacosDiscoveryHeartBeatInterval() {
assertThat(properties.getHeartBeatInterval()).isEqualTo(Integer.valueOf(3)); assertThat(properties.getHeartBeatInterval()).isEqualTo(Integer.valueOf(3000));
} }
private void checkoutNacosDiscoveryHeartBeatTimeout() { private void checkoutNacosDiscoveryHeartBeatTimeout() {
assertThat(properties.getHeartBeatTimeout()).isEqualTo(Integer.valueOf(6)); assertThat(properties.getHeartBeatTimeout()).isEqualTo(Integer.valueOf(6000));
} }
private void checkoutNacosDiscoveryIpDeleteTimeout() { private void checkoutNacosDiscoveryIpDeleteTimeout() {
assertThat(properties.getIpDeleteTimeout()).isEqualTo(Integer.valueOf(9)); assertThat(properties.getIpDeleteTimeout()).isEqualTo(Integer.valueOf(9000));
} }
private void checkoutNacosDiscoveryServiceName() { private void checkoutNacosDiscoveryServiceName() {
...@@ -207,7 +211,7 @@ public class NacosAutoServiceRegistrationTests { ...@@ -207,7 +211,7 @@ public class NacosAutoServiceRegistrationTests {
private void checkoutEndpoint() throws Exception { private void checkoutEndpoint() throws Exception {
NacosDiscoveryEndpoint nacosDiscoveryEndpoint = new NacosDiscoveryEndpoint( NacosDiscoveryEndpoint nacosDiscoveryEndpoint = new NacosDiscoveryEndpoint(
properties); nacosServiceManager, properties);
Map<String, Object> map = nacosDiscoveryEndpoint.nacosDiscovery(); Map<String, Object> map = nacosDiscoveryEndpoint.nacosDiscovery();
assertThat(properties).isEqualTo(map.get("NacosDiscoveryProperties")); assertThat(properties).isEqualTo(map.get("NacosDiscoveryProperties"));
......
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.nacos.registry;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Properties;
import com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration;
import com.alibaba.nacos.api.NacosFactory;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.api.support.MethodProxy;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
* @author L.cm
*/
@RunWith(PowerMockRunner.class)
@PowerMockIgnore("javax.management.*")
@PowerMockRunnerDelegate(SpringRunner.class)
@PrepareForTest({ NacosFactory.class })
@SpringBootTest(classes = NacosRegistrationCustomizerTest.TestConfig.class,
properties = { "spring.application.name=myTestService1",
"spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848" },
webEnvironment = RANDOM_PORT)
public class NacosRegistrationCustomizerTest {
@Autowired
private NacosAutoServiceRegistration nacosAutoServiceRegistration;
static {
try {
Method method = PowerMockito.method(NacosFactory.class, "createNamingService",
Properties.class);
MethodProxy.proxy(method, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return new MockNamingService();
}
});
}
catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void contextLoads() throws Exception {
NacosRegistration registration = nacosAutoServiceRegistration.getRegistration();
Map<String, String> metadata = registration.getMetadata();
Assert.assertEquals("test1", metadata.get("test1"));
}
@Configuration
@EnableAutoConfiguration
@ImportAutoConfiguration({ AutoServiceRegistrationConfiguration.class,
NacosDiscoveryClientConfiguration.class,
NacosServiceRegistryAutoConfiguration.class })
public static class TestConfig {
@Bean
public NacosRegistrationCustomizer nacosRegistrationCustomizer() {
return registration -> {
Map<String, String> metadata = registration.getMetadata();
metadata.put("test1", "test1");
};
}
}
}
...@@ -40,7 +40,8 @@ import org.springframework.test.context.junit4.SpringRunner; ...@@ -40,7 +40,8 @@ import org.springframework.test.context.junit4.SpringRunner;
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(classes = NacosRibbonClientPropertyOverrideTests.TestConfiguration.class, @SpringBootTest(classes = NacosRibbonClientPropertyOverrideTests.TestConfiguration.class,
properties = { "spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848", properties = { "spring.cloud.nacos.server-addr=127.0.0.1:8848",
"spring.cloud.nacos.username=nacos", "spring.cloud.nacos.password=nacos",
"spring.cloud.nacos.discovery.port=18080", "spring.cloud.nacos.discovery.port=18080",
"spring.cloud.nacos.discovery.service=remoteApp", "spring.cloud.nacos.discovery.service=remoteApp",
"localApp.ribbon.NIWSServerListClassName=" "localApp.ribbon.NIWSServerListClassName="
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>spring-cloud-timeloit-starters</artifactId> <artifactId>spring-cloud-timeloit-starters</artifactId>
<groupId>com.timeloit.cloud</groupId> <groupId>com.timeloit.cloud</groupId>
<version>2.3.3-SNAPSHOT</version> <version>2.2.8-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
...@@ -176,4 +176,4 @@ ...@@ -176,4 +176,4 @@
</dependencies> </dependencies>
</project> </project>
\ No newline at end of file
...@@ -37,7 +37,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties ...@@ -37,7 +37,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/** /**
...@@ -66,23 +65,6 @@ public class SentinelWebAutoConfiguration implements WebMvcConfigurer { ...@@ -66,23 +65,6 @@ public class SentinelWebAutoConfiguration implements WebMvcConfigurer {
@Autowired @Autowired
private Optional<RequestOriginParser> requestOriginParserOptional; private Optional<RequestOriginParser> requestOriginParserOptional;
@Autowired
private Optional<SentinelWebInterceptor> sentinelWebInterceptorOptional;
@Override
public void addInterceptors(InterceptorRegistry registry) {
if (!sentinelWebInterceptorOptional.isPresent()) {
return;
}
SentinelProperties.Filter filterConfig = properties.getFilter();
registry.addInterceptor(sentinelWebInterceptorOptional.get())
.order(filterConfig.getOrder())
.addPathPatterns(filterConfig.getUrlPatterns());
log.info(
"[Sentinel Starter] register SentinelWebInterceptor with urlPatterns: {}.",
filterConfig.getUrlPatterns());
}
@Bean @Bean
@ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled", @ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled",
matchIfMissing = true) matchIfMissing = true)
...@@ -122,4 +104,11 @@ public class SentinelWebAutoConfiguration implements WebMvcConfigurer { ...@@ -122,4 +104,11 @@ public class SentinelWebAutoConfiguration implements WebMvcConfigurer {
return sentinelWebMvcConfig; return sentinelWebMvcConfig;
} }
@Bean
@ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled",
matchIfMissing = true)
public SentinelWebMvcConfigurer sentinelWebMvcConfigurer() {
return new SentinelWebMvcConfigurer();
}
} }
...@@ -14,53 +14,44 @@ ...@@ -14,53 +14,44 @@
* limitations under the License. * limitations under the License.
*/ */
package com.alibaba.cloud.nacos.parser; package com.alibaba.cloud.sentinel;
import java.io.BufferedReader; import java.util.Optional;
import java.io.IOException;
import java.io.StringReader;
import java.util.LinkedHashMap;
import java.util.Map;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/** /**
* @author zkz * @author: chao.wu
*/ */
public class NacosDataPropertiesParser extends AbstractNacosDataParser { public class SentinelWebMvcConfigurer implements WebMvcConfigurer {
private static final Logger log = LoggerFactory private static final Logger log = LoggerFactory
.getLogger(NacosDataPropertiesParser.class); .getLogger(SentinelWebMvcConfigurer.class);
public NacosDataPropertiesParser() { @Autowired
super("properties"); private SentinelProperties sentinelProperties;
}
@Override @Autowired
protected Map<String, Object> doParse(String data) throws IOException { private Optional<SentinelWebInterceptor> sentinelWebInterceptorOptional;
Map<String, Object> result = new LinkedHashMap<>();
try (BufferedReader reader = new BufferedReader(new StringReader(data))) { @Override
for (String line = reader.readLine(); line != null; line = reader public void addInterceptors(InterceptorRegistry registry) {
.readLine()) { if (!sentinelWebInterceptorOptional.isPresent()) {
String dataLine = line.trim(); return;
if (StringUtils.isEmpty(dataLine) || dataLine.startsWith("#")) {
continue;
}
int index = dataLine.indexOf("=");
if (index == -1) {
log.warn("the config data is invalid {}", dataLine);
continue;
}
String key = dataLine.substring(0, index);
String value = dataLine.substring(index + 1);
result.put(key.trim(), value.trim());
}
} }
return result; SentinelProperties.Filter filterConfig = sentinelProperties.getFilter();
registry.addInterceptor(sentinelWebInterceptorOptional.get())
.order(filterConfig.getOrder())
.addPathPatterns(filterConfig.getUrlPatterns());
log.info(
"[Sentinel Starter] register SentinelWebInterceptor with urlPatterns: {}.",
filterConfig.getUrlPatterns());
} }
} }
...@@ -86,13 +86,23 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor ...@@ -86,13 +86,23 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor
Tracer.trace( Tracer.trace(
new IllegalStateException("RestTemplate ErrorHandler has error")); new IllegalStateException("RestTemplate ErrorHandler has error"));
} }
return response;
} }
catch (Throwable e) { catch (Throwable e) {
if (!BlockException.isBlockException(e)) { if (BlockException.isBlockException(e)) {
Tracer.trace(e); return handleBlockException(request, body, execution, (BlockException) e);
} }
else { else {
return handleBlockException(request, body, execution, (BlockException) e); Tracer.traceEntry(e, hostEntry);
if (e instanceof IOException) {
throw (IOException) e;
}
else if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
else {
throw new IOException(e);
}
} }
} }
finally { finally {
...@@ -103,7 +113,6 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor ...@@ -103,7 +113,6 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor
hostEntry.exit(); hostEntry.exit();
} }
} }
return response;
} }
private ClientHttpResponse handleBlockException(HttpRequest request, byte[] body, private ClientHttpResponse handleBlockException(HttpRequest request, byte[] body,
......
...@@ -25,7 +25,7 @@ import com.alibaba.csp.sentinel.datasource.AbstractDataSource; ...@@ -25,7 +25,7 @@ import com.alibaba.csp.sentinel.datasource.AbstractDataSource;
import com.alibaba.csp.sentinel.heartbeat.HeartbeatSenderProvider; import com.alibaba.csp.sentinel.heartbeat.HeartbeatSenderProvider;
import com.alibaba.csp.sentinel.transport.HeartbeatSender; import com.alibaba.csp.sentinel.transport.HeartbeatSender;
import com.alibaba.csp.sentinel.transport.config.TransportConfig; import com.alibaba.csp.sentinel.transport.config.TransportConfig;
import com.alibaba.csp.sentinel.util.function.Tuple2; import com.alibaba.csp.sentinel.transport.endpoint.Endpoint;
import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.actuate.health.AbstractHealthIndicator; import org.springframework.boot.actuate.health.AbstractHealthIndicator;
...@@ -84,8 +84,7 @@ public class SentinelHealthIndicator extends AbstractHealthIndicator { ...@@ -84,8 +84,7 @@ public class SentinelHealthIndicator extends AbstractHealthIndicator {
// Check health of Dashboard // Check health of Dashboard
boolean dashboardUp = true; boolean dashboardUp = true;
List<Tuple2<String, Integer>> consoleServerList = TransportConfig List<Endpoint> consoleServerList = TransportConfig.getConsoleServerList();
.getConsoleServerList();
if (CollectionUtils.isEmpty(consoleServerList)) { if (CollectionUtils.isEmpty(consoleServerList)) {
// If Dashboard isn't configured, it's OK and mark the status of Dashboard // If Dashboard isn't configured, it's OK and mark the status of Dashboard
// with UNKNOWN. // with UNKNOWN.
...@@ -105,7 +104,7 @@ public class SentinelHealthIndicator extends AbstractHealthIndicator { ...@@ -105,7 +104,7 @@ public class SentinelHealthIndicator extends AbstractHealthIndicator {
// If failed to send heartbeat message, means that the Dashboard is DOWN // If failed to send heartbeat message, means that the Dashboard is DOWN
dashboardUp = false; dashboardUp = false;
detailMap.put("dashboard", detailMap.put("dashboard",
new Status(Status.DOWN.getCode(), String.format( new Status(Status.UNKNOWN.getCode(), String.format(
"the dashboard servers [%s] one of them can't be connected", "the dashboard servers [%s] one of them can't be connected",
consoleServerList))); consoleServerList)));
} }
...@@ -138,7 +137,7 @@ public class SentinelHealthIndicator extends AbstractHealthIndicator { ...@@ -138,7 +137,7 @@ public class SentinelHealthIndicator extends AbstractHealthIndicator {
// DOWN // DOWN
dataSourceUp = false; dataSourceUp = false;
dataSourceDetailMap.put(dataSourceBeanName, dataSourceDetailMap.put(dataSourceBeanName,
new Status(Status.DOWN.getCode(), e.getMessage())); new Status(Status.UNKNOWN.getCode(), e.getMessage()));
} }
} }
...@@ -147,7 +146,7 @@ public class SentinelHealthIndicator extends AbstractHealthIndicator { ...@@ -147,7 +146,7 @@ public class SentinelHealthIndicator extends AbstractHealthIndicator {
builder.up().withDetails(detailMap); builder.up().withDetails(detailMap);
} }
else { else {
builder.down().withDetails(detailMap); builder.unknown().withDetails(detailMap);
} }
} }
......
...@@ -80,34 +80,37 @@ public final class SentinelFeign { ...@@ -80,34 +80,37 @@ public final class SentinelFeign {
// using reflect get fallback and fallbackFactory properties from // using reflect get fallback and fallbackFactory properties from
// FeignClientFactoryBean because FeignClientFactoryBean is a package // FeignClientFactoryBean because FeignClientFactoryBean is a package
// level class, we can not use it in our package // level class, we can not use it in our package
Object feignClientFactoryBean = Builder.this.applicationContext Object feignClientFactoryBean = SentinelTargeterAspect
.getBean("&" + target.type().getName()); .getFeignClientFactoryBean();
Class fallback = (Class) getFieldValue(feignClientFactoryBean, if (feignClientFactoryBean != null) {
"fallback"); Class fallback = (Class) getFieldValue(feignClientFactoryBean,
Class fallbackFactory = (Class) getFieldValue(feignClientFactoryBean, "fallback");
"fallbackFactory"); Class fallbackFactory = (Class) getFieldValue(
String beanName = (String) getFieldValue(feignClientFactoryBean, feignClientFactoryBean, "fallbackFactory");
"contextId"); String beanName = (String) getFieldValue(feignClientFactoryBean,
if (!StringUtils.hasText(beanName)) { "contextId");
beanName = (String) getFieldValue(feignClientFactoryBean, "name"); if (!StringUtils.hasText(beanName)) {
} beanName = (String) getFieldValue(feignClientFactoryBean,
"name");
Object fallbackInstance; }
FallbackFactory fallbackFactoryInstance;
// check fallback and fallbackFactory properties Object fallbackInstance;
if (void.class != fallback) { FallbackFactory fallbackFactoryInstance;
fallbackInstance = getFromContext(beanName, "fallback", fallback, // check fallback and fallbackFactory properties
target.type()); if (void.class != fallback) {
return new SentinelInvocationHandler(target, dispatch, fallbackInstance = getFromContext(beanName, "fallback",
new FallbackFactory.Default(fallbackInstance)); fallback, target.type());
} return new SentinelInvocationHandler(target, dispatch,
if (void.class != fallbackFactory) { new FallbackFactory.Default(fallbackInstance));
fallbackFactoryInstance = (FallbackFactory) getFromContext( }
beanName, "fallbackFactory", fallbackFactory, if (void.class != fallbackFactory) {
FallbackFactory.class); fallbackFactoryInstance = (FallbackFactory) getFromContext(
return new SentinelInvocationHandler(target, dispatch, beanName, "fallbackFactory", fallbackFactory,
fallbackFactoryInstance); FallbackFactory.class);
return new SentinelInvocationHandler(target, dispatch,
fallbackFactoryInstance);
}
} }
return new SentinelInvocationHandler(target, dispatch); return new SentinelInvocationHandler(target, dispatch);
} }
......
...@@ -41,4 +41,11 @@ public class SentinelFeignAutoConfiguration { ...@@ -41,4 +41,11 @@ public class SentinelFeignAutoConfiguration {
return SentinelFeign.builder(); return SentinelFeign.builder();
} }
@Bean
@ConditionalOnProperty(name = "feign.sentinel.enabled")
@ConditionalOnClass(name = "org.springframework.cloud.openfeign.Targeter")
public SentinelTargeterAspect sentinelTargeterAspect() {
return new SentinelTargeterAspect();
}
} }
...@@ -109,7 +109,7 @@ public class SentinelInvocationHandler implements InvocationHandler { ...@@ -109,7 +109,7 @@ public class SentinelInvocationHandler implements InvocationHandler {
catch (Throwable ex) { catch (Throwable ex) {
// fallback handle // fallback handle
if (!BlockException.isBlockException(ex)) { if (!BlockException.isBlockException(ex)) {
Tracer.trace(ex); Tracer.traceEntry(ex, entry);
} }
if (fallbackFactory != null) { if (fallbackFactory != null) {
try { try {
......
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.sentinel.feign;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
/**
* Record FeignClientFactoryBean to threadlocal, so that SentinelFeign can get it when
* creating SentinelInvocationHandler.
*
* @see com.alibaba.cloud.sentinel.feign.SentinelFeign.Builder
* @author <a href="mailto:chenxilzx1@gmail.com">theonefx</a>
*/
@Aspect
public class SentinelTargeterAspect {
private static final ThreadLocal<Object> FEIGN_CLIENT_FACTORY_BEAN = new ThreadLocal<>();
public static Object getFeignClientFactoryBean() {
return FEIGN_CLIENT_FACTORY_BEAN.get();
}
@Around("execution(* org.springframework.cloud.openfeign.Targeter.target(..))")
public Object process(ProceedingJoinPoint pjp) throws Throwable {
Object factory = pjp.getArgs()[0];
try {
FEIGN_CLIENT_FACTORY_BEAN.set(factory);
return pjp.proceed();
}
finally {
FEIGN_CLIENT_FACTORY_BEAN.remove();
}
}
}
...@@ -31,7 +31,8 @@ import com.alibaba.csp.sentinel.slots.block.RuleConstant; ...@@ -31,7 +31,8 @@ import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.transport.config.TransportConfig; import com.alibaba.csp.sentinel.transport.config.TransportConfig;
import com.alibaba.csp.sentinel.util.function.Tuple2; import com.alibaba.csp.sentinel.transport.endpoint.Endpoint;
import com.alibaba.csp.sentinel.transport.endpoint.Protocol;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -134,8 +135,10 @@ public class SentinelAutoConfigurationTests { ...@@ -134,8 +135,10 @@ public class SentinelAutoConfigurationTests {
Map<String, Object> map = sentinelEndpoint.invoke(); Map<String, Object> map = sentinelEndpoint.invoke();
assertThat(map.get("logUsePid")).isEqualTo(Boolean.TRUE); assertThat(map.get("logUsePid")).isEqualTo(Boolean.TRUE);
assertThat(map.get("consoleServer").toString()).isEqualTo( assertThat(map.get("consoleServer").toString())
Arrays.asList(Tuple2.of("localhost", 8080), Tuple2.of("localhost", 8081)) .isEqualTo(Arrays
.asList(new Endpoint(Protocol.HTTP, "localhost", 8080),
new Endpoint(Protocol.HTTP, "localhost", 8081))
.toString()); .toString());
assertThat(map.get("clientPort")).isEqualTo("9999"); assertThat(map.get("clientPort")).isEqualTo("9999");
assertThat(map.get("heartbeatIntervalMs")).isEqualTo(20000L); assertThat(map.get("heartbeatIntervalMs")).isEqualTo(20000L);
...@@ -185,8 +188,10 @@ public class SentinelAutoConfigurationTests { ...@@ -185,8 +188,10 @@ public class SentinelAutoConfigurationTests {
@Test @Test
public void testSentinelSystemProperties() { public void testSentinelSystemProperties() {
assertThat(LogBase.isLogNameUsePid()).isEqualTo(true); assertThat(LogBase.isLogNameUsePid()).isEqualTo(true);
assertThat(TransportConfig.getConsoleServerList().toString()).isEqualTo( assertThat(TransportConfig.getConsoleServerList().toString())
Arrays.asList(Tuple2.of("localhost", 8080), Tuple2.of("localhost", 8081)) .isEqualTo(Arrays
.asList(new Endpoint(Protocol.HTTP, "localhost", 8080),
new Endpoint(Protocol.HTTP, "localhost", 8081))
.toString()); .toString());
assertThat(TransportConfig.getPort()).isEqualTo("9999"); assertThat(TransportConfig.getPort()).isEqualTo("9999");
assertThat(TransportConfig.getHeartbeatIntervalMs().longValue()) assertThat(TransportConfig.getHeartbeatIntervalMs().longValue())
......
...@@ -109,9 +109,9 @@ public class SentinelHealthIndicatorTests { ...@@ -109,9 +109,9 @@ public class SentinelHealthIndicatorTests {
Health health = sentinelHealthIndicator.health(); Health health = sentinelHealthIndicator.health();
assertThat(health.getStatus()).isEqualTo(Status.DOWN); assertThat(health.getStatus()).isEqualTo(Status.UNKNOWN);
assertThat(health.getDetails().get("dashboard")).isEqualTo( assertThat(health.getDetails().get("dashboard")).isEqualTo(new Status(
new Status(Status.DOWN.getCode(), "localhost:8080 can't be connected")); Status.UNKNOWN.getCode(), "localhost:8080 can't be connected"));
} }
@Test @Test
...@@ -163,13 +163,13 @@ public class SentinelHealthIndicatorTests { ...@@ -163,13 +163,13 @@ public class SentinelHealthIndicatorTests {
Health health = sentinelHealthIndicator.health(); Health health = sentinelHealthIndicator.health();
assertThat(health.getStatus()).isEqualTo(Status.DOWN); assertThat(health.getStatus()).isEqualTo(Status.UNKNOWN);
Map<String, Status> dataSourceDetailMap = (Map<String, Status>) health Map<String, Status> dataSourceDetailMap = (Map<String, Status>) health
.getDetails().get("dataSource"); .getDetails().get("dataSource");
assertThat(dataSourceDetailMap.get("ds1-sentinel-file-datasource")) assertThat(dataSourceDetailMap.get("ds1-sentinel-file-datasource"))
.isEqualTo(Status.UP); .isEqualTo(Status.UP);
assertThat(dataSourceDetailMap.get("ds2-sentinel-file-datasource")) assertThat(dataSourceDetailMap.get("ds2-sentinel-file-datasource"))
.isEqualTo(new Status(Status.DOWN.getCode(), "fileDataSource2 error")); .isEqualTo(new Status(Status.UNKNOWN.getCode(), "fileDataSource2 error"));
} }
} }
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-timeloit-starters</artifactId>
<groupId>com.timeloit.cloud</groupId>
<version>2.2.8-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-timeloit-commons</artifactId>
<name>Spring Cloud Timeloit Commons</name>
</project>
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.io;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* The Charsets constants, copy from apache commons-io.
*
* @author <a href="mailto:chenxilzx1@gmail.com">theonefx</a>
*/
public final class Charsets {
private Charsets() {
}
/**
* Constructs a sorted map from canonical charset names to charset objects required of
* every implementation of the Java platform.
* <p>
* From the Java documentation
* <a href="https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">
* Standard charsets</a>:
* </p>
* @return An immutable, case-insensitive map from canonical charset names to charset
* objects.
* @see Charset#availableCharsets()
*/
public static SortedMap<String, Charset> requiredCharsets() {
// maybe cache?
final TreeMap<String, Charset> m = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
m.put(StandardCharsets.ISO_8859_1.name(), StandardCharsets.ISO_8859_1);
m.put(StandardCharsets.US_ASCII.name(), StandardCharsets.US_ASCII);
m.put(StandardCharsets.UTF_16.name(), StandardCharsets.UTF_16);
m.put(StandardCharsets.UTF_16BE.name(), StandardCharsets.UTF_16BE);
m.put(StandardCharsets.UTF_16LE.name(), StandardCharsets.UTF_16LE);
m.put(StandardCharsets.UTF_8.name(), StandardCharsets.UTF_8);
return Collections.unmodifiableSortedMap(m);
}
/**
* Returns the given Charset or the default Charset if the given Charset is null.
* @param charset A charset or null.
* @return the given Charset or the default Charset if the given Charset is null
*/
public static Charset toCharset(final Charset charset) {
return charset == null ? Charset.defaultCharset() : charset;
}
/**
* Returns a Charset for the named charset. If the name is null, return the default
* Charset.
* @param charset The name of the requested charset, may be null.
* @return a Charset for the named charset
* @throws java.nio.charset.UnsupportedCharsetException If the named charset is
* unavailable
*/
public static Charset toCharset(final String charset) {
return charset == null ? Charset.defaultCharset() : Charset.forName(charset);
}
/**
* CharEncodingISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.
* <p>
* Every implementation of the Java platform is required to support this character
* encoding.
* </p>
*
* @see <a href=
* "https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard
* charsets</a>
* @deprecated Use Java 7's {@link StandardCharsets}
*/
@Deprecated
public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
/**
* <p>
* Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of
* the Unicode character set.
* </p>
* <p>
* Every implementation of the Java platform is required to support this character
* encoding.
* </p>
*
* @see <a href=
* "https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard
* charsets</a>
* @deprecated Use Java 7's {@link StandardCharsets}
*/
@Deprecated
public static final Charset US_ASCII = StandardCharsets.US_ASCII;
/**
* <p>
* Sixteen-bit Unicode Transformation Format, The byte order specified by a mandatory
* initial byte-order mark (either order accepted on input, big-endian used on output)
* </p>
* <p>
* Every implementation of the Java platform is required to support this character
* encoding.
* </p>
*
* @see <a href=
* "https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard
* charsets</a>
* @deprecated Use Java 7's {@link StandardCharsets}
*/
@Deprecated
public static final Charset UTF_16 = StandardCharsets.UTF_16;
/**
* <p>
* Sixteen-bit Unicode Transformation Format, big-endian byte order.
* </p>
* <p>
* Every implementation of the Java platform is required to support this character
* encoding.
* </p>
*
* @see <a href=
* "https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard
* charsets</a>
* @deprecated Use Java 7's {@link StandardCharsets}
*/
@Deprecated
public static final Charset UTF_16BE = StandardCharsets.UTF_16BE;
/**
* <p>
* Sixteen-bit Unicode Transformation Format, little-endian byte order.
* </p>
* <p>
* Every implementation of the Java platform is required to support this character
* encoding.
* </p>
*
* @see <a href=
* "https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard
* charsets</a>
* @deprecated Use Java 7's {@link StandardCharsets}
*/
@Deprecated
public static final Charset UTF_16LE = StandardCharsets.UTF_16LE;
/**
* <p>
* Eight-bit Unicode Transformation Format.
* </p>
* <p>
* Every implementation of the Java platform is required to support this character
* encoding.
* </p>
*
* @see <a href=
* "https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard
* charsets</a>
* @deprecated Use Java 7's {@link StandardCharsets}
*/
@Deprecated
public static final Charset UTF_8 = StandardCharsets.UTF_8;
}
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.io;
import java.io.*;
import java.nio.charset.Charset;
/**
* FileUtils. copy from apache commons-io.
*
* @author <a href="mailto:chenxilzx1@gmail.com">theonefx</a>
*/
public final class FileUtils {
private FileUtils() {
}
// -----------------------------------------------------------------------
/**
* Opens a {@link FileInputStream} for the specified file, providing better
* error messages than simply calling <code>new FileInputStream(file)</code>.
* <p>
* At the end of the method either the stream will be successfully opened, or an
* exception will have been thrown.
* <p>
* An exception is thrown if the file does not exist. An exception is thrown if the
* file object exists but is a directory. An exception is thrown if the file exists
* but cannot be read.
* @param file the file to open for input, must not be {@code null}
* @return a new {@link FileInputStream} for the specified file
* @throws FileNotFoundException if the file does not exist
* @throws IOException if the file object is a directory
* @throws IOException if the file cannot be read
* @since 1.3
*/
public static FileInputStream openInputStream(final File file) throws IOException {
if (file.exists()) {
if (file.isDirectory()) {
throw new IOException("File '" + file + "' exists but is a directory");
}
if (!file.canRead()) {
throw new IOException("File '" + file + "' cannot be read");
}
}
else {
throw new FileNotFoundException("File '" + file + "' does not exist");
}
return new FileInputStream(file);
}
// -----------------------------------------------------------------------
/**
* Reads the contents of a file into a String. The file is always closed.
* @param file the file to read, must not be {@code null}
* @param encoding the encoding to use, {@code null} means platform default
* @return the file contents, never {@code null}
* @throws IOException in case of an I/O error
*/
public static String readFileToString(final File file, final Charset encoding)
throws IOException {
try (InputStream in = openInputStream(file)) {
return IOUtils.toString(in, Charsets.toCharset(encoding));
}
}
/**
* Reads the contents of a file into a String. The file is always closed.
* @param file the file to read, must not be {@code null}
* @param encoding the encoding to use, {@code null} means platform default
* @return the file contents, never {@code null}
* @throws IOException in case of an I/O error
* @throws java.nio.charset.UnsupportedCharsetException thrown instead of
* {@link java.io .UnsupportedEncodingException} in version 2.2 if the encoding is not
* supported.
*/
public static String readFileToString(final File file, final String encoding)
throws IOException {
return readFileToString(file, Charsets.toCharset(encoding));
}
/**
* Reads the contents of a file into a String using the default encoding for the VM.
* The file is always closed.
* @param file the file to read, must not be {@code null}
* @return the file contents, never {@code null}
* @throws IOException in case of an I/O error
* @deprecated 2.5 use {@link #readFileToString(File, String)} instead (and specify
* the appropriate encoding)
*/
@Deprecated
public static String readFileToString(final File file) throws IOException {
return readFileToString(file, Charset.defaultCharset());
}
}
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.io;
import java.io.*;
import java.nio.charset.Charset;
/**
* The IOUtils. copy from apache commons-io.
*
* @author <a href="mailto:chenxilzx1@gmail.com">theonefx</a>
*/
public final class IOUtils {
/**
* Represents the end-of-file (or stream).
* @since 2.5 (made public)
*/
public static final int EOF = -1;
/**
* The default buffer size ({@value}) to use for.
* {@link #copyLarge(InputStream, OutputStream)} and
* {@link #copyLarge(Reader, Writer)}
*/
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
private IOUtils() {
}
/**
* Gets the contents of an <code>InputStream</code> as a String using the specified
* character encoding.
* <p>
* This method buffers the input internally, so there is no need to use a
* <code>BufferedInputStream</code>.
* </p>
* @param input the <code>InputStream</code> to read from
* @param encoding the encoding to use, null means platform default
* @return the requested String
* @throws NullPointerException if the input is null
* @throws IOException if an I/O error occurs
* @since 2.3
*/
public static String toString(final InputStream input, final Charset encoding)
throws IOException {
try (StringBuilderWriter sw = new StringBuilderWriter()) {
copy(input, sw, encoding);
return sw.toString();
}
}
// copy from Reader
// -----------------------------------------------------------------------
/**
* Copies chars from a <code>Reader</code> to a <code>Writer</code>.
* <p>
* This method buffers the input internally, so there is no need to use a
* <code>BufferedReader</code>.
* <p>
* Large streams (over 2GB) will return a chars copied value of <code>-1</code> after
* the copy has completed since the correct number of chars cannot be returned as an
* int. For large streams use the <code>copyLarge(Reader, Writer)</code> method.
* @param input the <code>Reader</code> to read from
* @param output the <code>Writer</code> to write to
* @return the number of characters copied, or -1 if &gt; Integer.MAX_VALUE
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
* @since 1.1
*/
public static int copy(final Reader input, final Writer output) throws IOException {
final long count = copyLarge(input, output);
if (count > Integer.MAX_VALUE) {
return -1;
}
return (int) count;
}
/**
* Copies bytes from an <code>InputStream</code> to chars on a <code>Writer</code>
* using the specified character encoding.
* <p>
* This method buffers the input internally, so there is no need to use a
* <code>BufferedInputStream</code>.
* <p>
* This method uses {@link InputStreamReader}.
* @param input the <code>InputStream</code> to read from
* @param output the <code>Writer</code> to write to
* @param inputEncoding the encoding to use for the input stream, null means platform
* default
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
* @since 2.3
*/
public static void copy(final InputStream input, final Writer output,
final Charset inputEncoding) throws IOException {
final InputStreamReader in = new InputStreamReader(input,
Charsets.toCharset(inputEncoding));
copy(in, output);
}
/**
* Copies bytes from an <code>InputStream</code> to an <code>OutputStream</code> using
* an internal buffer of the given size.
* <p>
* This method buffers the input internally, so there is no need to use a
* <code>BufferedInputStream</code>.
* <p>
* @param input the <code>InputStream</code> to read from
* @param output the <code>OutputStream</code> to write to
* @param bufferSize the bufferSize used to copy from the input to the output
* @return the number of bytes copied
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
* @since 2.5
*/
public static long copy(final InputStream input, final OutputStream output,
final int bufferSize) throws IOException {
return copyLarge(input, output, new byte[bufferSize]);
}
/**
* Copies chars from a large (over 2GB) <code>Reader</code> to a <code>Writer</code>.
* <p>
* This method buffers the input internally, so there is no need to use a
* <code>BufferedReader</code>.
* <p>
* The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}.
* @param input the <code>Reader</code> to read from
* @param output the <code>Writer</code> to write to
* @return the number of characters copied
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
* @since 1.3
*/
public static long copyLarge(final Reader input, final Writer output)
throws IOException {
return copyLarge(input, output, new char[DEFAULT_BUFFER_SIZE]);
}
/**
* Copies chars from a large (over 2GB) <code>Reader</code> to a <code>Writer</code>.
* <p>
* This method uses the provided buffer, so there is no need to use a
* <code>BufferedReader</code>.
* <p>
* @param input the <code>Reader</code> to read from
* @param output the <code>Writer</code> to write to
* @param buffer the buffer to be used for the copy
* @return the number of characters copied
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
* @since 2.2
*/
public static long copyLarge(final Reader input, final Writer output,
final char[] buffer) throws IOException {
long count = 0;
int n;
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
/**
* Copies bytes from a large (over 2GB) <code>InputStream</code> to an
* <code>OutputStream</code>.
* <p>
* This method buffers the input internally, so there is no need to use a
* <code>BufferedInputStream</code>.
* <p>
* The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}.
* @param input the <code>InputStream</code> to read from
* @param output the <code>OutputStream</code> to write to
* @return the number of bytes copied
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
* @since 1.3
*/
public static long copyLarge(final InputStream input, final OutputStream output)
throws IOException {
return copy(input, output, DEFAULT_BUFFER_SIZE);
}
/**
* Copies bytes from a large (over 2GB) <code>InputStream</code> to an
* <code>OutputStream</code>.
* <p>
* This method uses the provided buffer, so there is no need to use a
* <code>BufferedInputStream</code>.
* <p>
* @param input the <code>InputStream</code> to read from
* @param output the <code>OutputStream</code> to write to
* @param buffer the buffer to use for the copy
* @return the number of bytes copied
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
* @since 2.2
*/
public static long copyLarge(final InputStream input, final OutputStream output,
final byte[] buffer) throws IOException {
long count = 0;
int n;
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
}
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.io;
import java.io.Serializable;
import java.io.Writer;
/**
* Copy from apache commons-io.
*
* @author <a href="mailto:chenxilzx1@gmail.com">theonefx</a>
*/
public class StringBuilderWriter extends Writer implements Serializable {
private static final long serialVersionUID = -146927496096066153L;
private final StringBuilder builder;
/**
* Constructs a new {@link StringBuilder} instance with default capacity.
*/
public StringBuilderWriter() {
this.builder = new StringBuilder();
}
/**
* Constructs a new {@link StringBuilder} instance with the specified capacity.
* @param capacity The initial capacity of the underlying {@link StringBuilder}
*/
public StringBuilderWriter(final int capacity) {
this.builder = new StringBuilder(capacity);
}
/**
* Constructs a new instance with the specified {@link StringBuilder}.
*
* <p>
* If {@code builder} is null a new instance with default capacity will be created.
* </p>
* @param builder The String builder. May be null.
*/
public StringBuilderWriter(final StringBuilder builder) {
this.builder = builder != null ? builder : new StringBuilder();
}
/**
* Appends a single character to this Writer.
* @param value The character to append
* @return This writer instance
*/
@Override
public Writer append(final char value) {
builder.append(value);
return this;
}
/**
* Appends a character sequence to this Writer.
* @param value The character to append
* @return This writer instance
*/
@Override
public Writer append(final CharSequence value) {
builder.append(value);
return this;
}
/**
* Appends a portion of a character sequence to the {@link StringBuilder}.
* @param value The character to append
* @param start The index of the first character
* @param end The index of the last character + 1
* @return This writer instance
*/
@Override
public Writer append(final CharSequence value, final int start, final int end) {
builder.append(value, start, end);
return this;
}
/**
* Closing this writer has no effect.
*/
@Override
public void close() {
// no-op
}
/**
* Flushing this writer has no effect.
*/
@Override
public void flush() {
// no-op
}
/**
* Writes a String to the {@link StringBuilder}.
* @param value The value to write
*/
@Override
public void write(final String value) {
if (value != null) {
builder.append(value);
}
}
/**
* Writes a portion of a character array to the {@link StringBuilder}.
* @param value The value to write
* @param offset The index of the first character
* @param length The number of characters to write
*/
@Override
public void write(final char[] value, final int offset, final int length) {
if (value != null) {
builder.append(value, offset, length);
}
}
/**
* Returns the underlying builder.
* @return The underlying builder
*/
public StringBuilder getBuilder() {
return builder;
}
/**
* Returns {@link StringBuilder#toString()}.
* @return The contents of the String builder.
*/
@Override
public String toString() {
return builder.toString();
}
}
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.commons.lang;
/**
* StringUtils. copy from apache common-lang3.
*
* @author <a href="mailto:chenxilzx1@gmail.com">theonefx</a>
*/
public final class StringUtils {
/**
* The empty String {@code ""}.
*
* @since 2.0
*/
public static final String EMPTY = "";
/**
* Represents a failed index search.
* @since 2.1
*/
public static final int INDEX_NOT_FOUND = -1;
private StringUtils() {
}
/**
* <p>
* Checks if a CharSequence is empty ("") or null.
* </p>
*
* <pre>
* StringUtils.isEmpty(null) = true
* StringUtils.isEmpty("") = true
* StringUtils.isEmpty(" ") = false
* StringUtils.isEmpty("bob") = false
* StringUtils.isEmpty(" bob ") = false
* </pre>
*
* <p>
* NOTE: This method changed in Lang version 2.0. It no longer trims the CharSequence.
* That functionality is available in isBlank().
* </p>
* @param cs the CharSequence to check, may be null
* @return {@code true} if the CharSequence is empty or null
* @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence)
*/
public static boolean isEmpty(final CharSequence cs) {
return cs == null || cs.length() == 0;
}
/**
* <p>
* Checks if a CharSequence is not empty ("") and not null.
* </p>
*
* <pre>
* StringUtils.isNotEmpty(null) = false
* StringUtils.isNotEmpty("") = false
* StringUtils.isNotEmpty(" ") = true
* StringUtils.isNotEmpty("bob") = true
* StringUtils.isNotEmpty(" bob ") = true
* </pre>
* @param cs the CharSequence to check, may be null
* @return {@code true} if the CharSequence is not empty and not null
* @since 3.0 Changed signature from isNotEmpty(String) to isNotEmpty(CharSequence)
*/
public static boolean isNotEmpty(final CharSequence cs) {
return !isEmpty(cs);
}
/**
* <p>
* Checks if a CharSequence is whitespace, empty ("") or null.
* </p>
*
* <pre>
* StringUtils.isBlank(null) = true
* StringUtils.isBlank("") = true
* StringUtils.isBlank(" ") = true
* StringUtils.isBlank("bob") = false
* StringUtils.isBlank(" bob ") = false
* </pre>
* @param cs the CharSequence to check, may be null
* @return {@code true} if the CharSequence is null, empty or whitespace
*/
public static boolean isBlank(final CharSequence cs) {
if (cs == null || cs.length() == 0) {
return true;
}
int strLen = cs.length();
for (int i = 0; i < strLen; i++) {
if (!Character.isWhitespace(cs.charAt(i))) {
return false;
}
}
return true;
}
/**
* <p>
* Checks if a CharSequence is not empty (""), not null and not whitespace only.
* </p>
*
* <p>
* Whitespace is defined by {@link Character#isWhitespace(char)}.
* </p>
*
* <pre>
* StringUtils.isNotBlank(null) = false
* StringUtils.isNotBlank("") = false
* StringUtils.isNotBlank(" ") = false
* StringUtils.isNotBlank("bob") = true
* StringUtils.isNotBlank(" bob ") = true
* </pre>
* @param cs the CharSequence to check, may be null
* @return {@code true} if the CharSequence is not empty and not null and not
* whitespace only
* @since 2.0
* @since 3.0 Changed signature from isNotBlank(String) to isNotBlank(CharSequence)
*/
public static boolean isNotBlank(final CharSequence cs) {
return !isBlank(cs);
}
// Trim
// -----------------------------------------------------------------------
/**
* <p>
* Removes control characters (char &lt;= 32) from both ends of this String, handling
* {@code null} by returning {@code null}.
* </p>
*
* <p>
* The String is trimmed using {@link String#trim()}. Trim removes start and end
* characters &lt;= 32.
* </p>
*
* <pre>
* StringUtils.trim(null) = null
* StringUtils.trim("") = ""
* StringUtils.trim(" ") = ""
* StringUtils.trim("abc") = "abc"
* StringUtils.trim(" abc ") = "abc"
* </pre>
* @param str the String to be trimmed, may be null
* @return the trimmed string, {@code null} if null String input
*/
public static String trim(final String str) {
return str == null ? null : str.trim();
}
// Equals
// -----------------------------------------------------------------------
/**
* <p>
* Compares two CharSequences, returning {@code true} if they represent equal
* sequences of characters.
* </p>
*
* <p>
* {@code null}s are handled without exceptions. Two {@code null} references are
* considered to be equal. The comparison is case sensitive.
* </p>
*
* <pre>
* StringUtils.equals(null, null) = true
* StringUtils.equals(null, "abc") = false
* StringUtils.equals("abc", null) = false
* StringUtils.equals("abc", "abc") = true
* StringUtils.equals("abc", "ABC") = false
* </pre>
* @param cs1 the first CharSequence, may be {@code null}
* @param cs2 the second CharSequence, may be {@code null}
* @return {@code true} if the CharSequences are equal (case-sensitive), or both
* {@code null}
* @see Object#equals(Object)
*/
public static boolean equals(final CharSequence cs1, final CharSequence cs2) {
if (cs1 == cs2) {
return true;
}
if (cs1 == null || cs2 == null) {
return false;
}
if (cs1 instanceof String && cs2 instanceof String) {
return cs1.equals(cs2);
}
return StringUtils.regionMatches(cs1, false, 0, cs2, 0,
Math.max(cs1.length(), cs2.length()));
}
/**
* Green implementation of regionMatches.
* @param cs the {@code CharSequence} to be processed
* @param ignoreCase whether or not to be case insensitive
* @param thisStart the index to start on the {@code cs} CharSequence
* @param substring the {@code CharSequence} to be looked for
* @param start the index to start on the {@code substring} CharSequence
* @param length character length of the region
* @return whether the region matched
*/
public static boolean regionMatches(final CharSequence cs, final boolean ignoreCase,
final int thisStart, final CharSequence substring, final int start,
final int length) {
if (cs instanceof String && substring instanceof String) {
return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring,
start, length);
}
int index1 = thisStart;
int index2 = start;
int tmpLen = length;
while (tmpLen-- > 0) {
final char c1 = cs.charAt(index1++);
final char c2 = substring.charAt(index2++);
if (c1 == c2) {
continue;
}
if (!ignoreCase) {
return false;
}
// The same check as in String.regionMatches():
if (Character.toUpperCase(c1) != Character.toUpperCase(c2)
&& Character.toLowerCase(c1) != Character.toLowerCase(c2)) {
return false;
}
}
return true;
}
/**
* <p>
* Gets the substring after the first occurrence of a separator. The separator is not
* returned.
* </p>
*
* <p>
* A <code>null</code> string input will return <code>null</code>. An empty ("")
* string input will return the empty string. A <code>null</code> separator will
* return the empty string if the input string is not <code>null</code>.
* </p>
*
* <p>
* If nothing is found, the empty string is returned.
* </p>
*
* <pre>
* StringUtils.substringAfter(null, *) = null
* StringUtils.substringAfter("", *) = ""
* StringUtils.substringAfter(*, null) = ""
* StringUtils.substringAfter("abc", "a") = "bc"
* StringUtils.substringAfter("abcba", "b") = "cba"
* StringUtils.substringAfter("abc", "c") = ""
* StringUtils.substringAfter("abc", "d") = ""
* StringUtils.substringAfter("abc", "") = "abc"
* </pre>
* @param str the String to get a substring from, may be null
* @param separator the String to search for, may be null
* @return the substring after the first occurrence of the separator,
* <code>null</code> if null String input
* @since 2.0
*/
public static String substringAfter(String str, String separator) {
if (isEmpty(str)) {
return str;
}
if (separator == null) {
return EMPTY;
}
int pos = str.indexOf(separator);
if (pos == INDEX_NOT_FOUND) {
return EMPTY;
}
return str.substring(pos + separator.length());
}
// Substring between
// -----------------------------------------------------------------------
/**
* <p>
* Gets the String that is nested in between two instances of the same String.
* </p>
*
* <p>
* A <code>null</code> input String returns <code>null</code>. A <code>null</code> tag
* returns <code>null</code>.
* </p>
*
* <pre>
* StringUtils.substringBetween(null, *) = null
* StringUtils.substringBetween("", "") = ""
* StringUtils.substringBetween("", "tag") = null
* StringUtils.substringBetween("tagabctag", null) = null
* StringUtils.substringBetween("tagabctag", "") = ""
* StringUtils.substringBetween("tagabctag", "tag") = "abc"
* </pre>
* @param str the String containing the substring, may be null
* @param tag the String before and after the substring, may be null
* @return the substring, <code>null</code> if no match
* @since 2.0
*/
public static String substringBetween(String str, String tag) {
return substringBetween(str, tag, tag);
}
/**
* <p>
* Gets the String that is nested in between two Strings. Only the first match is
* returned.
* </p>
*
* <p>
* A <code>null</code> input String returns <code>null</code>. A <code>null</code>
* open/close returns <code>null</code> (no match). An empty ("") open and close
* returns an empty string.
* </p>
*
* <pre>
* StringUtils.substringBetween("wx[b]yz", "[", "]") = "b"
* StringUtils.substringBetween(null, *, *) = null
* StringUtils.substringBetween(*, null, *) = null
* StringUtils.substringBetween(*, *, null) = null
* StringUtils.substringBetween("", "", "") = ""
* StringUtils.substringBetween("", "", "]") = null
* StringUtils.substringBetween("", "[", "]") = null
* StringUtils.substringBetween("yabcz", "", "") = ""
* StringUtils.substringBetween("yabcz", "y", "z") = "abc"
* StringUtils.substringBetween("yabczyabcz", "y", "z") = "abc"
* </pre>
* @param str the String containing the substring, may be null
* @param open the String before the substring, may be null
* @param close the String after the substring, may be null
* @return the substring, <code>null</code> if no match
* @since 2.0
*/
public static String substringBetween(String str, String open, String close) {
if (str == null || open == null || close == null) {
return null;
}
int start = str.indexOf(open);
if (start != INDEX_NOT_FOUND) {
int end = str.indexOf(close, start + open.length());
if (end != INDEX_NOT_FOUND) {
return str.substring(start + open.length(), end);
}
}
return null;
}
}
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>spring-cloud-timeloit-starters</artifactId> <artifactId>spring-cloud-timeloit-starters</artifactId>
<groupId>com.timeloit.cloud</groupId> <groupId>com.timeloit.cloud</groupId>
<version>2.3.3-SNAPSHOT</version> <version>2.2.8-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
...@@ -14,6 +14,10 @@ ...@@ -14,6 +14,10 @@
<dependencies> <dependencies>
<dependency>
<groupId>com.timeloit.cloud</groupId>
<artifactId>spring-cloud-timeloit-commons</artifactId>
</dependency>
<!--spring boot --> <!--spring boot -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
...@@ -133,4 +137,4 @@ ...@@ -133,4 +137,4 @@
</dependencies> </dependencies>
</project> </project>
\ No newline at end of file
...@@ -132,7 +132,6 @@ public class DataSourcePropertiesConfiguration { ...@@ -132,7 +132,6 @@ public class DataSourcePropertiesConfiguration {
if (!ObjectUtils.isEmpty(field.get(this))) { if (!ObjectUtils.isEmpty(field.get(this))) {
return field.getName(); return field.getName();
} }
return null;
} }
catch (IllegalAccessException e) { catch (IllegalAccessException e) {
// won't happen // won't happen
......
...@@ -32,6 +32,8 @@ public class NacosDataSourceProperties extends AbstractDataSourceProperties { ...@@ -32,6 +32,8 @@ public class NacosDataSourceProperties extends AbstractDataSourceProperties {
private String serverAddr; private String serverAddr;
private String contextPath;
private String username; private String username;
private String password; private String password;
...@@ -71,6 +73,14 @@ public class NacosDataSourceProperties extends AbstractDataSourceProperties { ...@@ -71,6 +73,14 @@ public class NacosDataSourceProperties extends AbstractDataSourceProperties {
this.serverAddr = serverAddr; this.serverAddr = serverAddr;
} }
public String getContextPath() {
return contextPath;
}
public void setContextPath(String contextPath) {
this.contextPath = contextPath;
}
public String getUsername() { public String getUsername() {
return username; return username;
} }
......
...@@ -86,9 +86,8 @@ public abstract class SentinelConverter<T extends Object> ...@@ -86,9 +86,8 @@ public abstract class SentinelConverter<T extends Object>
}); });
for (Object obj : sourceArray) { for (Object obj : sourceArray) {
String item = null;
try { try {
item = objectMapper.writeValueAsString(obj); String item = objectMapper.writeValueAsString(obj);
Optional.ofNullable(convertRule(item)) Optional.ofNullable(convertRule(item))
.ifPresent(convertRule -> ruleCollection.add(convertRule)); .ifPresent(convertRule -> ruleCollection.add(convertRule));
} }
......
...@@ -35,6 +35,8 @@ public class NacosDataSourceFactoryBean implements FactoryBean<NacosDataSource> ...@@ -35,6 +35,8 @@ public class NacosDataSourceFactoryBean implements FactoryBean<NacosDataSource>
private String serverAddr; private String serverAddr;
private String contextPath;
private String username; private String username;
private String password; private String password;
...@@ -60,22 +62,27 @@ public class NacosDataSourceFactoryBean implements FactoryBean<NacosDataSource> ...@@ -60,22 +62,27 @@ public class NacosDataSourceFactoryBean implements FactoryBean<NacosDataSource>
properties.setProperty(PropertyKeyConst.SERVER_ADDR, this.serverAddr); properties.setProperty(PropertyKeyConst.SERVER_ADDR, this.serverAddr);
} }
else { else {
properties.setProperty(PropertyKeyConst.ENDPOINT, this.endpoint);
}
if (!StringUtils.isEmpty(this.contextPath)) {
properties.setProperty(PropertyKeyConst.CONTEXT_PATH, this.contextPath);
}
if (!StringUtils.isEmpty(this.accessKey)) {
properties.setProperty(PropertyKeyConst.ACCESS_KEY, this.accessKey); properties.setProperty(PropertyKeyConst.ACCESS_KEY, this.accessKey);
}
if (!StringUtils.isEmpty(this.secretKey)) {
properties.setProperty(PropertyKeyConst.SECRET_KEY, this.secretKey); properties.setProperty(PropertyKeyConst.SECRET_KEY, this.secretKey);
properties.setProperty(PropertyKeyConst.ENDPOINT, this.endpoint);
} }
if (!StringUtils.isEmpty(this.namespace)) { if (!StringUtils.isEmpty(this.namespace)) {
properties.setProperty(PropertyKeyConst.NAMESPACE, this.namespace); properties.setProperty(PropertyKeyConst.NAMESPACE, this.namespace);
} }
if (!StringUtils.isEmpty(this.username)) { if (!StringUtils.isEmpty(this.username)) {
properties.setProperty(PropertyKeyConst.USERNAME, this.username); properties.setProperty(PropertyKeyConst.USERNAME, this.username);
} }
if (!StringUtils.isEmpty(this.password)) { if (!StringUtils.isEmpty(this.password)) {
properties.setProperty(PropertyKeyConst.PASSWORD, this.password); properties.setProperty(PropertyKeyConst.PASSWORD, this.password);
} }
return new NacosDataSource(properties, groupId, dataId, converter); return new NacosDataSource(properties, groupId, dataId, converter);
} }
...@@ -92,6 +99,14 @@ public class NacosDataSourceFactoryBean implements FactoryBean<NacosDataSource> ...@@ -92,6 +99,14 @@ public class NacosDataSourceFactoryBean implements FactoryBean<NacosDataSource>
this.serverAddr = serverAddr; this.serverAddr = serverAddr;
} }
public String getContextPath() {
return contextPath;
}
public void setContextPath(String contextPath) {
this.contextPath = contextPath;
}
public String getUsername() { public String getUsername() {
return username; return username;
} }
......
...@@ -16,9 +16,9 @@ ...@@ -16,9 +16,9 @@
package com.alibaba.cloud.sentinel.datasource.factorybean; package com.alibaba.cloud.sentinel.datasource.factorybean;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource; import com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.FactoryBean;
......
...@@ -39,6 +39,8 @@ public class NacosDataSourceFactoryBeanTests { ...@@ -39,6 +39,8 @@ public class NacosDataSourceFactoryBeanTests {
private String serverAddr = "localhost:8848"; private String serverAddr = "localhost:8848";
private String contextPath = "/my-nacos";
private String accessKey = "ak"; private String accessKey = "ak";
private String secretKey = "sk"; private String secretKey = "sk";
...@@ -56,6 +58,7 @@ public class NacosDataSourceFactoryBeanTests { ...@@ -56,6 +58,7 @@ public class NacosDataSourceFactoryBeanTests {
factoryBean.setDataId(dataId); factoryBean.setDataId(dataId);
factoryBean.setGroupId(groupId); factoryBean.setGroupId(groupId);
factoryBean.setServerAddr(serverAddr); factoryBean.setServerAddr(serverAddr);
factoryBean.setContextPath(contextPath);
factoryBean.setConverter(converter); factoryBean.setConverter(converter);
NacosDataSource nacosDataSource = mock(NacosDataSource.class); NacosDataSource nacosDataSource = mock(NacosDataSource.class);
...@@ -69,6 +72,7 @@ public class NacosDataSourceFactoryBeanTests { ...@@ -69,6 +72,7 @@ public class NacosDataSourceFactoryBeanTests {
assertThat(factoryBean.getDataId()).isEqualTo(dataId); assertThat(factoryBean.getDataId()).isEqualTo(dataId);
assertThat(factoryBean.getGroupId()).isEqualTo(groupId); assertThat(factoryBean.getGroupId()).isEqualTo(groupId);
assertThat(factoryBean.getServerAddr()).isEqualTo(serverAddr); assertThat(factoryBean.getServerAddr()).isEqualTo(serverAddr);
assertThat(factoryBean.getContextPath()).isEqualTo(contextPath);
} }
@Test @Test
......
...@@ -31,11 +31,13 @@ public class NacosDataSourcePropertiesTests { ...@@ -31,11 +31,13 @@ public class NacosDataSourcePropertiesTests {
public void testNacosWithAddr() { public void testNacosWithAddr() {
NacosDataSourceProperties nacosDataSourceProperties = new NacosDataSourceProperties(); NacosDataSourceProperties nacosDataSourceProperties = new NacosDataSourceProperties();
nacosDataSourceProperties.setServerAddr("127.0.0.1:8848"); nacosDataSourceProperties.setServerAddr("127.0.0.1:8848");
nacosDataSourceProperties.setContextPath("/my-nacos");
nacosDataSourceProperties.setRuleType(RuleType.FLOW); nacosDataSourceProperties.setRuleType(RuleType.FLOW);
nacosDataSourceProperties.setDataId("sentinel"); nacosDataSourceProperties.setDataId("sentinel");
nacosDataSourceProperties.setGroupId("custom-group"); nacosDataSourceProperties.setGroupId("custom-group");
nacosDataSourceProperties.setDataType("xml"); nacosDataSourceProperties.setDataType("xml");
assertThat(nacosDataSourceProperties.getContextPath()).isEqualTo("/my-nacos");
assertThat(nacosDataSourceProperties.getGroupId()).isEqualTo("custom-group"); assertThat(nacosDataSourceProperties.getGroupId()).isEqualTo("custom-group");
assertThat(nacosDataSourceProperties.getDataId()).isEqualTo("sentinel"); assertThat(nacosDataSourceProperties.getDataId()).isEqualTo("sentinel");
assertThat(nacosDataSourceProperties.getDataType()).isEqualTo("xml"); assertThat(nacosDataSourceProperties.getDataType()).isEqualTo("xml");
......
...@@ -19,13 +19,13 @@ package com.alibaba.cloud.sentinel.datasource; ...@@ -19,13 +19,13 @@ package com.alibaba.cloud.sentinel.datasource;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import com.alibaba.cloud.commons.io.FileUtils;
import com.alibaba.cloud.sentinel.datasource.converter.JsonConverter; import com.alibaba.cloud.sentinel.datasource.converter.JsonConverter;
import com.alibaba.cloud.sentinel.datasource.converter.XmlConverter; import com.alibaba.cloud.sentinel.datasource.converter.XmlConverter;
import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.apache.commons.io.FileUtils;
import org.junit.Test; import org.junit.Test;
import org.springframework.util.ResourceUtils; import org.springframework.util.ResourceUtils;
......
# Sentinel Spring Cloud Zuul Adapter
Zuul does not provide rateLimit function, If use default `SentinelRibbonFilter` route filter. it wrapped by Hystrix Command. so only provide Service level
circuit protect.
Sentinel can provide `ServiceId` level and `API Path` level flow control for spring cloud zuul gateway service.
*Note*: this project is for zuul 1.
## How to use
1. Add maven dependency
```xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
<version>x.y.z</version>
</dependency>
```
2. Set application.property
```
// default value is false
spring.cloud.sentinel.zuul.enabled=true
```
## How it works
As Zuul run as per thread per connection block model, we add filters around `route Filter` to trace sentinel statistics.
- `SentinelPreFilter`: Get an entry of resource,the first order is **ServiceId**, then **API Path**.
- `SentinelPostFilter`: When success response,exit entry.
- `SentinelErrorFilter`: When get an `Exception`, trace the exception and exit context.
the order of Filter can be changed by configuration:
```
spring.cloud.sentinel.zuul.order.post=0
spring.cloud.sentinel.zuul.order.pre=10000
spring.cloud.sentinel.zuul.order.error=-1
```
Filters create structure like:
```bash
EntranceNode: machine-root(t:3 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
-EntranceNode: coke(t:2 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
--coke(t:2 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
---/coke/uri(t:0 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
-EntranceNode: sentinel_default_context(t:0 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
-EntranceNode: book(t:1 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
--book(t:1 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
---/book/uri(t:0 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
```
`book` and `coke` are serviceId.
`---/book/uri` is api path, the real uri is `/uri`.
## Integration with Sentinel DashBord
Start [Sentinel DashBord](https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0).
## Rule config with dataSource
Sentinel has full rule config features. see [Dynamic-Rule-Configuration](https://github.com/alibaba/Sentinel/wiki/Dynamic-Rule-Configuration)
## Custom Fallbacks
Implements `SentinelFallbackProvider` to define your own Fallback Provider when Sentinel Block Exception throwing for different rout. the default
Fallback Provider is `DefaultBlockFallbackProvider`.
By default Fallback route is `ServiveId + URI PATH`, example `/book/coke`, first `book` is serviceId, `/uri` is URI PATH, so both
can be needed.
Here is an example:
```java
// custom provider
public class MyCokeServiceBlockFallbackProvider implements SentinelFallbackProvider {
private Logger logger = LoggerFactory.getLogger(DefaultBlockFallbackProvider.class);
// you can define root as service level
@Override
public String getRoute() {
return "/coke/uri";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
if (cause instanceof BlockException) {
logger.info("get in fallback block exception:{}", cause);
return response(HttpStatus.TOO_MANY_REQUESTS, route);
} else {
return response(HttpStatus.INTERNAL_SERVER_ERROR, route);
}
}
}
```
## Custom Request Origin Parser
By default this adapter use `DefaultRequestOriginParser` to parse sentinel origin.
```java
public class CustomRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
// do custom logic.
return "";
}
}
```
## Custom UrlCleaner
By default this adapter use `DefaultUrlCleaner` to define uri resource.
```java
public class CustomUrlCleaner implements UrlCleaner {
@Override
public String clean(String originUrl) {
// do custom logic.
return originUrl;
}
}
```
\ No newline at end of file
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>spring-cloud-timeloit-starters</artifactId> <artifactId>spring-cloud-timeloit-starters</artifactId>
<groupId>com.timeloit.cloud</groupId> <groupId>com.timeloit.cloud</groupId>
<version>2.3.3-SNAPSHOT</version> <version>2.2.8-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
...@@ -77,4 +77,4 @@ ...@@ -77,4 +77,4 @@
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>
\ No newline at end of file
...@@ -37,7 +37,7 @@ public final class ConfigConstants { ...@@ -37,7 +37,7 @@ public final class ConfigConstants {
/** /**
* ConfigurationProperties for {@link SentinelZuulProperties}. * ConfigurationProperties for {@link SentinelZuulProperties}.
*/ */
public static final String ZUUl_PREFIX = "spring.cloud.sentinel.zuul"; public static final String ZUUL_PREFIX = "spring.cloud.sentinel.zuul";
/** /**
* ConfigurationProperties for {@link SentinelGatewayProperties}. * ConfigurationProperties for {@link SentinelGatewayProperties}.
......
...@@ -47,7 +47,7 @@ import org.springframework.context.annotation.Configuration; ...@@ -47,7 +47,7 @@ import org.springframework.context.annotation.Configuration;
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ZuulServlet.class) @ConditionalOnClass(ZuulServlet.class)
@ConditionalOnProperty(prefix = ConfigConstants.ZUUl_PREFIX, name = "enabled", @ConditionalOnProperty(prefix = ConfigConstants.ZUUL_PREFIX, name = "enabled",
havingValue = "true", matchIfMissing = true) havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(SentinelZuulProperties.class) @EnableConfigurationProperties(SentinelZuulProperties.class)
public class SentinelZuulAutoConfiguration { public class SentinelZuulAutoConfiguration {
......
...@@ -28,7 +28,7 @@ import org.springframework.boot.context.properties.NestedConfigurationProperty; ...@@ -28,7 +28,7 @@ import org.springframework.boot.context.properties.NestedConfigurationProperty;
/** /**
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a> * @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/ */
@ConfigurationProperties(prefix = ConfigConstants.ZUUl_PREFIX) @ConfigurationProperties(prefix = ConfigConstants.ZUUL_PREFIX)
public class SentinelZuulProperties { public class SentinelZuulProperties {
@NestedConfigurationProperty @NestedConfigurationProperty
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论