1
0
mirror of https://gitee.com/mirrors/Spring-Cloud-Alibaba.git synced 2021-06-26 13:25:11 +08:00

Compare commits

...

53 Commits

Author SHA1 Message Date
余黄彬 4190fbba61 Merge pull request #1920 from theonefx/master
version update to 2.2.4
2021-01-15 18:34:33 +08:00
theonefx 248cd7696b upgrade version to 2.2.4.RELEASE 2021-01-15 17:52:05 +08:00
theonefx a20c665497 update nacos client version 2 1.4.1 2021-01-15 17:40:18 +08:00
TheoneFx fe00047ea6 Merge pull request #1892 from onewe/fix/weblogic_classloader_leak
fix(NacosConfigManager): 修复weblogic中classloader泄漏问题
2021-01-13 16:44:18 +08:00
TheoneFx dec10f17fe Merge pull request #1903 from github-ganyu/master
Fix bug: NPE Exception.
2021-01-12 11:38:56 +08:00
Mercy Ma 0c3e7294d8 Merge pull request #1907 from theonefx/dubbonacos
use double register to resolve "No provider available" issue
2021-01-12 10:10:40 +08:00
theonefx eea48c73cc refactor method and class name 2021-01-12 09:47:44 +08:00
theonefx 6c0edee13e use starting status in eureka when first registry 2021-01-11 22:54:10 +08:00
theonefx fd8a6a6f6c #1805 use twice registry of dubbo 2021-01-08 15:42:03 +08:00
余黄彬 344fc8be51 Merge pull request #1904 from brothelul/fix-#1893
[Fix] Spring cloud alibaba 2.2.3版本使用端点/actuator/service-registry注销服务注册问题
2021-01-07 21:19:38 +08:00
chenglu b264579012 update the Nacos instance by real group name, not use the default group name 2021-01-07 17:48:57 +08:00
ganyu.gy f64d794017 Fix bug: NPE Exception.
https://github.com/apache/dubbo/issues/7079
2021-01-07 16:00:30 +08:00
TheoneFx 573e8ed6c5 Merge pull request #1876 from horizonzy/upgrade-nacos-client
[ISSUE-#1875] Upgrade nacos-client version to 1.4.0
2021-01-07 09:53:37 +08:00
余黄彬 0567f0f4e1 Merge pull request #1878 from horizonzy/fix-nacos-heart-param
[ISSUE-#1877] Fix nacos heart beat unit, second to millisecond.
2021-01-06 17:44:38 +08:00
TheoneFx 4dd45a4709 Merge pull request #1531 from zkzlx/config
[fix & enhance issue #1492 #1506 #1257 #1578 ]Improved nacos configuration parsing,
2021-01-06 14:39:25 +08:00
onewe 17b3b7f858 fix(NacosConfigManager): 格式化代码 2021-01-04 14:11:52 +08:00
onewe 0c35595b44 fix(NacosConfigManager): 修复weblogic中classloader泄漏问题
- NacosConfigManager增加销毁方法,关闭nacos相关线程池
2021-01-01 19:36:39 +08:00
horizonzy 076a1648a2 update test heart beat param value 2020-12-23 13:08:49 +08:00
horizonzy 71036479a1 update test heart beat param value 2020-12-23 12:58:09 +08:00
horizonzy 447171de80 modify the heart beat doc unit. second -> millisecond 2020-12-23 10:09:26 +08:00
horizonzy 802958b218 Revert "fix nacos heart beat unit, second to millisecond."
This reverts commit 0e388707
2020-12-23 09:51:42 +08:00
horizonzy 0e38870735 fix nacos heart beat unit, second to millisecond. 2020-12-21 22:06:17 +08:00
horizonzy abdd0d29cb upgrade nacos-client version 2020-12-18 11:02:10 +08:00
TheoneFx 625b44e185 Merge pull request #1856 from eden-yuan/master
不应该使用类实例访问静态成员\不应该直接使用不确定的值调用equals容易空指针\final static应该使用大写命名并且使用下划线
2020-12-08 10:06:54 +08:00
eden-yuan c19bd51584 不应该使用类实例访问静态成员\不应该直接使用不确定的值调用equals容易空指针\final static应该使用大写命名并且使用下划线 2020-12-07 17:17:41 +08:00
TheoneFx 569f3f02c1 Merge pull request #1845 from yuhuangbin/master
Fix Issue #1791
2020-12-01 17:53:10 +08:00
yuhuangbin ac0712d330 enhance 2020-12-01 17:46:25 +08:00
余黄彬 62b289f3ff Merge pull request #1831 from zongyl/master
update docs
2020-11-26 13:59:57 +08:00
余黄彬 2f3460f3f5 Merge pull request #3 from alibaba/master
update
2020-11-25 17:41:21 +08:00
theonefx.chenx 43a79105e0 update 2 SNAPSHOT version 2020-11-25 16:32:42 +08:00
zongyl bcc262db15 update docs 2020-11-24 10:59:19 +08:00
余黄彬 e8de5cc8aa Merge pull request #1823 from yuhuangbin/nacos
Optimize the way to get NacosWatch#ThreadPoolTaskScheduler
2020-11-20 14:54:12 +08:00
yuhuangbin 8f008191ad Optimize the way to get NacosWatch ThreadPoolTaskScheduler 2020-11-19 17:12:40 +08:00
zkzlx e26ef50af4 [pr #1531]build 2020-11-16 13:42:31 +08:00
zkzlx 359b3ea522 Merge remote-tracking branch 'origin/master' into config
# Conflicts:
#	spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/hystrix/SeataHystrixConcurrencyStrategy.java
2020-11-16 12:44:09 +08:00
余黄彬 336476b15d Merge pull request #1799 from wangliang181230/bugfix-hystrix-RootContext.bind-throw-xid-must-be-not-null
bugfix: fix compatibility issue with 'seata-all-1.4.0'
2020-11-03 13:30:19 +08:00
王良 fa49d5f03c bugfix: fix compatibility issues with 'seata-all-1.4.0' 2020-11-03 11:42:27 +08:00
Jim Fang 9abfee6737 Merge pull request #1780 from lltx/master
fix EndpointId logWarning
2020-10-26 17:10:25 +08:00
Jim Fang 956acad43e Merge pull request #1772 from Roger3581321/patch-1
Add one missing configuration item
2020-10-26 16:50:47 +08:00
Jim Fang 7316db75be Merge pull request #1771 from Flyfoxs/patch-1
update Seata example
2020-10-26 16:50:12 +08:00
Jim Fang b5b26481bd Merge pull request #1775 from qq32933432/master
modify Seata example
2020-10-26 16:47:41 +08:00
冷冷 6540555920 fix EndpointId logWarning 2020-10-15 13:33:48 +08:00
zhongguangxi 1934c4760d maven project name change 2020-10-11 23:31:37 +08:00
Roger3581321 25689b800e Add one missing configuration item
Since there is an important, yet missing option spring.cloud.nacos.discovery.enabled, I have added its description by following  the "More configuration items" format.
2020-10-09 14:54:30 +08:00
Flyfoxs 11f8f02de6 Timeout exception in feign first time calling
第一次Feign调用时, 由于初始化懒加载, 可能会有timeout异常. 之前会被mock异常掩盖了.
2020-10-09 11:12:54 +08:00
余黄彬 9fb88da84c Merge pull request #1 from alibaba/master
update
2020-09-19 09:10:53 +08:00
余黄彬 29b955fffb Update README-zh.md 2020-09-18 11:27:20 +08:00
余黄彬 6617c9aef8 Update README.md 2020-09-18 11:26:32 +08:00
Mercy Ma 96a3807721 Merge pull request #1749 from alibaba/2.2.3.RELEASE
[CodeBase] Merge branch 2.2.3.release to master
2020-09-18 10:27:07 +08:00
zkzlx 1710d827fb [pr #1531]Improved nacos configuration parsing 2020-08-03 14:22:15 +08:00
zkzlx d96b0759f4 [enhance issue #1492 ]Improved nacos configuration parsing, based on PropertySourceLoader---check 2020-06-19 09:22:01 +08:00
zkzlx 5135916175 [enhance issue #1492 ]Improved nacos configuration parsing, based on PropertySourceLoader---check 2020-06-18 18:28:25 +08:00
zkzlx c07701d98e [enhance issue #1492 ]Improved nacos configuration parsing, based on PropertySourceLoader 2020-06-17 18:54:00 +08:00
60 changed files with 1046 additions and 547 deletions
+1 -1
View File
@@ -70,7 +70,7 @@ Spring Cloud 使用 Maven 来构建,最快的使用方式是将本项目 clone
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
+1 -1
View File
@@ -71,7 +71,7 @@ These artifacts are available from Maven Central and Spring Release repository v
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
+1 -1
View File
@@ -80,7 +80,7 @@
<properties>
<!-- Project revision -->
<revision>2.2.3-SNAPSHOT</revision>
<revision>2.2.4.RELEASE</revision>
<!-- Dependency Versions -->
<spring-cloud-commons.version>2.2.5.RELEASE</spring-cloud-commons.version>
+2 -2
View File
@@ -18,10 +18,10 @@
<description>Spring Cloud Alibaba Dependencies</description>
<properties>
<revision>2.2.3.RELEASE</revision>
<revision>2.2.4.RELEASE</revision>
<sentinel.version>1.8.0</sentinel.version>
<seata.version>1.3.0</seata.version>
<nacos.client.version>1.3.3</nacos.client.version>
<nacos.client.version>1.4.1</nacos.client.version>
<nacos.config.version>0.8.0</nacos.config.version>
<spring.context.support.version>1.0.10</spring.context.support.version>
@@ -10,7 +10,7 @@ Spring Cloud Alibaba BOM 包含了它所使用的所有依赖的版本。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.3.RELEASE</version>
<version>2.2.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -8,7 +8,7 @@ If youre a Maven Central user, add our BOM to your pom.xml <dependencyManagem
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.3.RELEASE</version>
<version>2.2.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -1,6 +1,6 @@
== Introduction
Spring Cloud Alibaba aims to provide a one-stop solution for microservices development. This prjoect includes the required components for developing distributed applications and services, so that developers can develop distributed applications easily with the Spring Cloud programming models.
Spring Cloud Alibaba aims to provide a one-stop solution for microservices development. This project includes the required components for developing distributed applications and services, so that developers can develop distributed applications easily with the Spring Cloud programming models.
With Spring Cloud Alibaba, you only need to add a few annotations and configurations, and you will be able to use the distributed solutions of Alibaba for your applications, and build a distributed system of your own with Alibaba middleware.
@@ -16,4 +16,4 @@ The features of Spring Cloud Alibaba:
8. **Alibaba Cloud SchedulerX**accurate, highly reliable, and highly available scheduled job scheduling services with response time within seconds.
9. **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.
Spring Cloud Alibaba also provide rich https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples[examples].
Spring Cloud Alibaba also provide rich https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples[examples].
@@ -159,7 +159,7 @@ NOTE: Before you start the provider application, please start Nacos first. Refer
==== Start a Consumer Application
It might not be as easy as starting a provider application, because the consumer needs to call the RESTful service of the provider. In this example, we will use the most primitive way, that is,
combining the LoadBalanceClient and RestTemolate explicitly to access the RESTful service.
combining the LoadBalanceClient and RestTemplate explicitly to access the RESTful service.
You can refer to section 1.2 for pom.xml and application.properties configurations. The following is the sample code for starting a consumer application.
NOTE: You can also access the service by using RestTemplate and FeignClient with load balancing.
@@ -18,6 +18,7 @@ package com.alibaba.cloud.examples;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
@@ -34,7 +35,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -62,8 +65,12 @@ class UserConfig {
private String name;
private String hr;
private Map<String, Object> map;
private List<User> users;
public int getAge() {
return age;
}
@@ -88,10 +95,65 @@ class UserConfig {
this.map = map;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
public String getHr() {
return hr;
}
public void setHr(String hr) {
this.hr = hr;
}
@Override
public String toString() {
return "UserConfig{" + "age=" + age + ", name='" + name + '\'' + ", map=" + map
+ '}';
+ ", hr='" + hr + '\'' + ", users=" + users + '}';
}
public static class User {
private String name;
private String hr;
private String avg;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHr() {
return hr;
}
public void setHr(String hr) {
this.hr = hr;
}
public String getAvg() {
return avg;
}
public void setAvg(String avg) {
this.avg = avg;
}
@Override
public String toString() {
return "User{" + "name='" + name + '\'' + ", hr=" + hr + ", avg=" + avg + '}';
}
}
}
@@ -99,7 +161,7 @@ class UserConfig {
@Component
class SampleRunner implements ApplicationRunner {
@Value("${user.name}")
@Value("${user.name:zz}")
String userName;
@Value("${user.age:25}")
@@ -156,7 +218,10 @@ class SampleController {
@Autowired
private NacosConfigManager nacosConfigManager;
@Value("${user.name}")
@Autowired
private Environment environment;
@Value("${user.name:zz}")
String userName;
@Value("${user.age:25}")
@@ -168,6 +233,11 @@ class SampleController {
+ userConfig + "!" + nacosConfigManager.getConfigService();
}
@RequestMapping("/get/{name}")
public String getValue(@PathVariable String name) {
return String.valueOf(environment.getProperty(name));
}
@RequestMapping("/bool")
public boolean bool() {
return (Boolean) (userConfig.getMap().get("2"));
@@ -5,19 +5,26 @@ spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.username=nacos
spring.cloud.nacos.password=nacos
#spring.cloud.nacos.config.namespace=public
spring.cloud.nacos.config.name=test-aaa
spring.cloud.nacos.config.file-extension=yaml
#spring.cloud.nacos.config.refreshable-dataids=common.properties
#spring.cloud.nacos.config.shared-data-ids=common.properties,base-common.properties
spring.cloud.nacos.config.shared-configs[0]= common333.properties
spring.cloud.nacos.config.shared-configs[1].data-id= common111.properties
spring.cloud.nacos.config.shared-configs[1].group= GROUP_APP1
spring.cloud.nacos.config.shared-configs[1].refresh= true
spring.cloud.nacos.config.shared-configs[2]= common222.properties
#spring.cloud.nacos.config.shared-configs[0]= common333.properties
#spring.cloud.nacos.config.shared-configs[1].data-id= common111.properties
#spring.cloud.nacos.config.shared-configs[1].group= GROUP_APP1
#spring.cloud.nacos.config.shared-configs[1].refresh= true
#spring.cloud.nacos.config.shared-configs[2]= common222.properties
spring.cloud.nacos.config.shared-configs[0].data-id= test2.yaml
spring.cloud.nacos.config.shared-configs[0].refresh=true
#spring.cloud.nacos.config.ext-config[0]=ext.properties
spring.cloud.nacos.config.extension-configs[0].data-id= extension1.properties
spring.cloud.nacos.config.extension-configs[0].refresh= true
spring.cloud.nacos.config.extension-configs[1]= extension2.properties
spring.cloud.nacos.config.extension-configs[2].data-id= extension3.json
spring.cloud.nacos.config.extension-configs[0].refresh=true
spring.cloud.nacos.config.extension-configs[1].data-id= test1.yml
spring.cloud.nacos.config.extension-configs[1].refresh= true
@@ -207,6 +207,7 @@ Metadata|spring.cloud.nacos.discovery.metadata||Extended data, Configure using M
log name|spring.cloud.nacos.discovery.log-name||
endpoint|spring.cloud.nacos.discovery.endpoint||The domain name of a service, through which the server address can be dynamically obtained.
Integration Ribbon|ribbon.nacos.enabled|true|
enabled|spring.cloud.nacos.discovery.enabled|true|The switch to enable or disable nacos service discovery
@@ -83,7 +83,7 @@ public class RocketMQProduceApplication {
@Override
public void run(String... args) throws Exception {
if (this.bindingName.equals("output1")) {
if ("output1".equals(this.bindingName)) {
int count = 5;
for (int index = 1; index <= count; index++) {
String msgContent = "msg-" + index;
@@ -98,7 +98,7 @@ public class RocketMQProduceApplication {
}
}
}
else if (this.bindingName.equals("output3")) {
else if ("output3".equals(this.bindingName)) {
int count = 20;
for (int index = 1; index <= count; index++) {
String msgContent = "pullMsg-" + index;
@@ -5,6 +5,8 @@ spring.cloud.nacos.discovery.server-addr=localhost:8848
#feign.hystrix.enabled=true
#feign.sentinel.enabled=true
feign.client.config.default.connectTimeout=10000
feign.client.config.default.readTimeout=10000
logging.level.io.seata=debug
@@ -10,7 +10,7 @@
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order-service</artifactId>
<name>Spring Cloud Starter Alibaba Seata Example - Business Service</name>
<name>Spring Cloud Starter Alibaba Seata Example - Order Service</name>
<packaging>jar</packaging>
<dependencies>
@@ -53,4 +53,4 @@
</dependencies>
</project>
</project>
@@ -20,7 +20,6 @@ import com.alibaba.cloud.examples.service.EchoService;
/**
* @author lengleng
* @date 2019-08-01
* <p>
* sentinel 降级处理
*/
@@ -35,7 +34,7 @@ public class EchoServiceFallback implements EchoService {
/**
* 调用服务提供方的输出接口.
* @param str 用户输入
* @return
* @return String
*/
@Override
public String echo(String str) {
@@ -22,7 +22,6 @@ import org.springframework.stereotype.Component;
/**
* @author lengleng
* @date 2019-08-01
*/
@Component
public class EchoServiceFallbackFactory implements FallbackFactory<EchoServiceFallback> {
@@ -24,7 +24,6 @@ import org.springframework.web.bind.annotation.PathVariable;
/**
* @author lengleng
* @date 2019-08-01
* <p>
* example feign client
*/
@@ -22,7 +22,6 @@ import org.springframework.web.bind.annotation.RestController;
/**
* @author lengleng
* @date 2019-08-01
*/
@RestController
public class EchoController {
@@ -41,10 +41,10 @@ public class ZuulConfiguration {
@Override
public BlockResponse fallbackResponse(String route, Throwable cause) {
if (route.equals("my-service3")) {
if ("my-service3".equals(route)) {
return new BlockResponse(433, "Sentinel Block3", route);
}
else if (route.equals("my-service4")) {
else if ("my-service4".equals(route)) {
return new BlockResponse(444, "my-service4", route);
}
else {
@@ -52,8 +52,8 @@ import org.springframework.util.StreamUtils;
import org.springframework.web.servlet.HttpServletBean;
import org.springframework.web.util.UriComponents;
import static org.apache.commons.lang3.StringUtils.substringAfter;
import static org.apache.commons.lang3.StringUtils.substringBetween;
import static org.apache.commons.lang.StringUtils.substringAfter;
import static org.apache.commons.lang.StringUtils.substringBetween;
import static org.springframework.web.util.UriComponentsBuilder.fromUriString;
@WebServlet(urlPatterns = "/dsc/*")
@@ -101,6 +101,12 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<optional>true</optional>
</dependency>
<!--spring boot-->
<dependency>
@@ -37,7 +37,7 @@ public final class ConfigConstants {
/**
* 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}.
@@ -47,7 +47,7 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ZuulServlet.class)
@ConditionalOnProperty(prefix = ConfigConstants.ZUUl_PREFIX, name = "enabled",
@ConditionalOnProperty(prefix = ConfigConstants.ZUUL_PREFIX, name = "enabled",
havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(SentinelZuulProperties.class)
public class SentinelZuulAutoConfiguration {
@@ -28,7 +28,7 @@ import org.springframework.boot.context.properties.NestedConfigurationProperty;
/**
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
@ConfigurationProperties(prefix = ConfigConstants.ZUUl_PREFIX)
@ConfigurationProperties(prefix = ConfigConstants.ZUUL_PREFIX)
public class SentinelZuulProperties {
@NestedConfigurationProperty
@@ -18,6 +18,8 @@ package com.alibaba.cloud.nacos;
import java.util.Objects;
import javax.annotation.PreDestroy;
import com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureException;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
@@ -73,6 +75,14 @@ public class NacosConfigManager {
return service;
}
@PreDestroy
public void destroy() throws NacosException {
if (service != null) {
service.shutDown();
service = null;
}
}
public NacosConfigProperties getNacosConfigProperties() {
return nacosConfigProperties;
}
@@ -16,12 +16,16 @@
package com.alibaba.cloud.nacos.client;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.alibaba.cloud.nacos.NacosConfigProperties;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.util.CollectionUtils;
/**
* @author xiaojing
@@ -58,6 +62,32 @@ public class NacosPropertySource extends MapPropertySource {
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() {
return this.group;
}
@@ -16,9 +16,9 @@
package com.alibaba.cloud.nacos.client;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.List;
import com.alibaba.cloud.nacos.NacosPropertySourceRepository;
import com.alibaba.cloud.nacos.parser.NacosDataParserHandler;
@@ -27,6 +27,7 @@ import com.alibaba.nacos.api.exception.NacosException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.PropertySource;
import org.springframework.util.StringUtils;
/**
@@ -38,8 +39,6 @@ public class NacosPropertySourceBuilder {
private static final Logger log = LoggerFactory
.getLogger(NacosPropertySourceBuilder.class);
private static final Map<String, Object> EMPTY_MAP = new LinkedHashMap();
private ConfigService configService;
private long timeout;
@@ -71,14 +70,15 @@ public class NacosPropertySourceBuilder {
*/
NacosPropertySource build(String dataId, String group, String fileExtension,
boolean isRefreshable) {
Map<String, Object> p = loadNacosData(dataId, group, fileExtension);
NacosPropertySource nacosPropertySource = new NacosPropertySource(group, dataId,
p, new Date(), isRefreshable);
List<PropertySource<?>> propertySources = loadNacosData(dataId, group,
fileExtension);
NacosPropertySource nacosPropertySource = new NacosPropertySource(propertySources,
group, dataId, new Date(), isRefreshable);
NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource);
return nacosPropertySource;
}
private Map<String, Object> loadNacosData(String dataId, String group,
private List<PropertySource<?>> loadNacosData(String dataId, String group,
String fileExtension) {
String data = null;
try {
@@ -87,24 +87,23 @@ public class NacosPropertySourceBuilder {
log.warn(
"Ignore the empty nacos configuration and get it based on dataId[{}] & group[{}]",
dataId, group);
return EMPTY_MAP;
return Collections.emptyList();
}
if (log.isDebugEnabled()) {
log.debug(String.format(
"Loading nacos data, dataId: '%s', group: '%s', data: %s", dataId,
group, data));
}
Map<String, Object> dataMap = NacosDataParserHandler.getInstance()
.parseNacosData(data, fileExtension);
return dataMap == null ? EMPTY_MAP : dataMap;
return NacosDataParserHandler.getInstance().parseNacosData(dataId, data,
fileExtension);
}
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) {
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 {
loadSharedConfiguration(composite);
loadExtConfiguration(composite);
loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);
return composite;
}
@@ -156,16 +155,15 @@ public class NacosPropertySourceLocator implements PropertySourceLocator {
private void loadNacosConfiguration(final CompositePropertySource composite,
List<NacosConfigProperties.Config> configs) {
for (NacosConfigProperties.Config config : configs) {
String dataId = config.getDataId();
String fileExtension = dataId.substring(dataId.lastIndexOf(DOT) + 1);
loadNacosDataIfPresent(composite, dataId, config.getGroup(), fileExtension,
loadNacosDataIfPresent(composite, config.getDataId(), config.getGroup(),
NacosDataParserHandler.getInstance()
.getFileExtension(config.getDataId()),
config.isRefresh());
}
}
private void checkConfiguration(List<NacosConfigProperties.Config> configs,
String tips) {
String[] dataIds = new String[configs.size()];
for (int i = 0; i < configs.size(); i++) {
String dataId = configs.get(i).getDataId();
if (dataId == null || dataId.trim().length() == 0) {
@@ -173,10 +171,7 @@ public class NacosPropertySourceLocator implements PropertySourceLocator {
"the [ spring.cloud.nacos.config.%s[%s] ] must give a dataId",
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,
@@ -36,7 +36,7 @@ import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
*
* @author xiaojing
*/
@Endpoint(id = "nacos-config")
@Endpoint(id = "nacosconfig")
public class NacosConfigEndpoint {
private final NacosConfigProperties properties;
@@ -1,175 +0,0 @@
/*
* 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.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.springframework.util.StringUtils;
/**
* @author zkz
*/
public abstract class AbstractNacosDataParser {
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.
* @param extension file extension. json or xml or yml or yaml or properties
* @return valid or not
*/
public final boolean checkFileExtension(String extension) {
if (this.isLegal(extension.toLowerCase())) {
return true;
}
if (this.nextParser == null) {
return false;
}
return this.nextParser.checkFileExtension(extension);
}
/**
* Parsing nacos configuration content.
* @param data config data from Nacos
* @param extension file extension. json or xml or yml or yaml or properties
* @return result of Properties
* @throws IOException thrown if there is a problem parsing config.
*/
public final Map<String, Object> parseNacosData(String data, String extension)
throws IOException {
if (extension == null || extension.length() < 1) {
throw new IllegalStateException("The file extension cannot be empty");
}
if (this.isLegal(extension.toLowerCase())) {
return this.doParse(data);
}
if (this.nextParser == null) {
throw new IllegalStateException(getTips(extension));
}
return this.nextParser.parseNacosData(data, extension);
}
/**
* Core logic for parsing.
* @param data config from Nacos
* @return result of Properties
* @throws IOException thrown if there is a problem parsing config.
*/
protected abstract Map<String, Object> doParse(String data) 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,
String parentKey) {
Set<Map.Entry<String, Object>> entries = dataMap.entrySet();
for (Iterator<Map.Entry<String, Object>> iterator = entries.iterator(); iterator
.hasNext();) {
Map.Entry<String, Object> entry = iterator.next();
String key = entry.getKey();
Object value = entry.getValue();
String fullKey = StringUtils.isEmpty(parentKey) ? key : key.startsWith("[")
? parentKey.concat(key) : parentKey.concat(DOT).concat(key);
if (value instanceof Map) {
Map<String, Object> map = (Map<String, Object>) value;
flattenedMap(result, map, fullKey);
continue;
}
else if (value instanceof Collection) {
int count = 0;
Collection<Object> collection = (Collection<Object>) value;
for (Object object : collection) {
flattenedMap(result,
Collections.singletonMap("[" + (count++) + "]", object),
fullKey);
}
continue;
}
result.put(fullKey, value);
}
}
/**
* 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);
}
}
@@ -0,0 +1,129 @@
/*
* 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.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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 static com.alibaba.cloud.nacos.parser.NacosDataParserHandler.DOT;
/**
* 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
*/
public abstract class AbstractPropertySourceLoader implements PropertySourceLoader {
/**
* 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;
}
/**
* 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
public List<PropertySource<?>> load(String name, Resource resource)
throws IOException {
if (!canLoad(name, resource)) {
return Collections.emptyList();
}
return this.doLoad(name, resource);
}
/**
* 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
*/
protected abstract List<PropertySource<?>> doLoad(String name, Resource resource)
throws IOException;
protected void flattenedMap(Map<String, Object> result, Map<String, Object> dataMap,
String parentKey) {
if (dataMap == null || dataMap.isEmpty()) {
return;
}
Set<Entry<String, Object>> entries = dataMap.entrySet();
for (Iterator<Entry<String, Object>> iterator = entries.iterator(); iterator
.hasNext();) {
Map.Entry<String, Object> entry = iterator.next();
String key = entry.getKey();
Object value = entry.getValue();
String fullKey = StringUtils.isEmpty(parentKey) ? key : key.startsWith("[")
? parentKey.concat(key) : parentKey.concat(DOT).concat(key);
if (value instanceof Map) {
Map<String, Object> map = (Map<String, Object>) value;
flattenedMap(result, map, fullKey);
continue;
}
else if (value instanceof Collection) {
int count = 0;
Collection<Object> collection = (Collection<Object>) value;
for (Object object : collection) {
flattenedMap(result,
Collections.singletonMap("[" + (count++) + "]", object),
fullKey);
}
continue;
}
result.put(fullKey, value);
}
}
}
@@ -0,0 +1,60 @@
/*
* 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 org.springframework.core.io.ByteArrayResource;
/**
* Nacos-specific resource.
*
* @author zkz
*/
public class NacosByteArrayResource extends ByteArrayResource {
private String filename;
/**
* Create a new {@code ByteArrayResource}.
* @param byteArray the byte array to wrap
*/
public NacosByteArrayResource(byte[] byteArray) {
super(byteArray);
}
/**
* Create a new {@code ByteArrayResource} with a description.
* @param byteArray the byte array to wrap
* @param description where the byte array comes from
*/
public NacosByteArrayResource(byte[] byteArray, String description) {
super(byteArray, description);
}
public void setFilename(String filename) {
this.filename = filename;
}
/**
* This implementation always returns {@code null}, assuming that this resource type
* does not have a filename.
*/
@Override
public String getFilename() {
return null == this.filename ? this.getDescription() : this.filename;
}
}
@@ -1,66 +0,0 @@
/*
* 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.LinkedHashMap;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* @author zkz
* @author yuhuangbin
*/
public class NacosDataJsonParser extends AbstractNacosDataParser {
protected NacosDataJsonParser() {
super("json");
}
@Override
protected Map<String, Object> doParse(String data) throws IOException {
if (StringUtils.isEmpty(data)) {
return null;
}
Map<String, Object> map = parseJSON2Map(data);
return this.reloadMap(map);
}
/**
* JSON to Map.
* @param json json data
* @return the map convert by json string
* @throws IOException thrown if there is a problem parsing config.
*/
private Map<String, Object> parseJSON2Map(String json) throws IOException {
Map<String, Object> result = new LinkedHashMap<>(32);
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> nacosDataMap = mapper.readValue(json, LinkedHashMap.class);
if (CollectionUtils.isEmpty(nacosDataMap)) {
return result;
}
flattenedMap(result, nacosDataMap, EMPTY_STRING);
return result;
}
}
@@ -17,63 +17,139 @@
package com.alibaba.cloud.nacos.parser;
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.Objects;
import java.util.stream.Collectors;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
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;
/**
* @author zkz
*/
public final class NacosDataParserHandler {
private AbstractNacosDataParser parser;
/**
* symbol: dot.
*/
public static final String DOT = ".";
/**
* constant.
*/
public static final String VALUE = "value";
/**
* default extension.
*/
public static final String DEFAULT_EXTENSION = "properties";
private static List<PropertySourceLoader> propertySourceLoaders;
private NacosDataParserHandler() {
parser = this.createParser();
propertySourceLoaders = SpringFactoriesLoader
.loadFactories(PropertySourceLoader.class, getClass().getClassLoader());
}
/**
* Parsing nacos configuration content.
* @param data config from Nacos
* @param extension file extension. json or xml or yml or yaml or properties
* @return result of LinkedHashMap
* @param configName name of nacos-config
* @param configValue value from nacos-config
* @param extension identifies the type of configValue
* @return result of Map
* @throws IOException thrown if there is a problem parsing config.
*/
public Map<String, Object> parseNacosData(String data, String extension)
throws IOException {
if (null == parser) {
parser = this.createParser();
public List<PropertySource<?>> parseNacosData(String configName, String configValue,
String extension) throws IOException {
if (StringUtils.isEmpty(configValue)) {
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 = 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.
* @param dataIdAry array of dataId
* @return dataId handle success or not
* check the current extension can be processed.
* @param loader the propertySourceLoader
* @param extension file extension
* @return if can match extension
*/
public boolean checkDataId(String... dataIdAry) {
StringBuilder stringBuilder = new StringBuilder();
for (String dataId : dataIdAry) {
int idx = dataId.lastIndexOf(AbstractNacosDataParser.DOT);
if (idx > 0 && idx < dataId.length() - 1) {
String extension = dataId.substring(idx + 1);
if (parser.checkFileExtension(extension)) {
break;
}
}
// add tips
stringBuilder.append(dataId).append(",");
}
if (stringBuilder.length() > 0) {
String result = stringBuilder.substring(0, stringBuilder.length() - 1);
throw new IllegalStateException(AbstractNacosDataParser.getTips(result));
}
return true;
private boolean canLoadFileExtension(PropertySourceLoader loader, String extension) {
return Arrays.stream(loader.getFileExtensions())
.anyMatch((fileExtension) -> StringUtils.endsWithIgnoreCase(extension,
fileExtension));
}
private AbstractNacosDataParser createParser() {
return new NacosDataPropertiesParser().addNextParser(new NacosDataYamlParser())
.addNextParser(new NacosDataXmlParser())
.addNextParser(new NacosDataJsonParser());
/**
* @param name filename
* @return file extension, default {@code DEFAULT_EXTENSION} if don't get
*/
public String getFileExtension(String name) {
if (StringUtils.isEmpty(name)) {
return DEFAULT_EXTENSION;
}
int idx = name.lastIndexOf(DOT);
if (idx > 0 && idx < name.length() - 1) {
return name.substring(idx + 1);
}
return DEFAULT_EXTENSION;
}
private String getFileName(String name, String extension) {
if (StringUtils.isEmpty(extension)) {
return name;
}
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() {
@@ -1,66 +0,0 @@
/*
* 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.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.LinkedHashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
/**
* @author zkz
*/
public class NacosDataPropertiesParser extends AbstractNacosDataParser {
private static final Logger log = LoggerFactory
.getLogger(NacosDataPropertiesParser.class);
public NacosDataPropertiesParser() {
super("properties");
}
@Override
protected Map<String, Object> doParse(String data) throws IOException {
Map<String, Object> result = new LinkedHashMap<>();
try (BufferedReader reader = new BufferedReader(new StringReader(data))) {
for (String line = reader.readLine(); line != null; line = reader
.readLine()) {
String dataLine = line.trim();
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;
}
}
@@ -1,44 +0,0 @@
/*
* 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.util.LinkedHashMap;
import java.util.Map;
import org.springframework.beans.factory.config.YamlMapFactoryBean;
import org.springframework.core.io.ByteArrayResource;
/**
* @author zkz
*/
public class NacosDataYamlParser extends AbstractNacosDataParser {
public NacosDataYamlParser() {
super(",yml,yaml,");
}
@Override
protected Map<String, Object> doParse(String data) {
YamlMapFactoryBean yamlFactory = new YamlMapFactoryBean();
yamlFactory.setResources(new ByteArrayResource(data.getBytes()));
Map<String, Object> result = new LinkedHashMap<>();
flattenedMap(result, yamlFactory.getObject(), EMPTY_STRING);
return result;
}
}
@@ -0,0 +1,92 @@
/*
* 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;
import static com.alibaba.cloud.nacos.parser.NacosDataParserHandler.DOT;
import static com.alibaba.cloud.nacos.parser.NacosDataParserHandler.VALUE;
/**
* @author zkz
*/
public class NacosJsonPropertySourceLoader extends AbstractPropertySourceLoader {
/**
* 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 @@
package com.alibaba.cloud.nacos.parser;
import java.io.IOException;
import java.io.StringReader;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
@@ -28,39 +29,76 @@ import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
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;
/**
* With relatively few usage scenarios, only simple parsing is performed to reduce jar
* dependencies.
* Parsing for XML requires overwriting the default
* {@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
*/
public class NacosDataXmlParser extends AbstractNacosDataParser {
public NacosDataXmlParser() {
super("xml");
}
public class NacosXmlPropertySourceLoader extends AbstractPropertySourceLoader
implements Ordered {
/**
* 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
protected Map<String, Object> doParse(String data) throws IOException {
if (StringUtils.isEmpty(data)) {
return null;
}
Map<String, Object> map = parseXml2Map(data);
return this.reloadMap(map);
public int getOrder() {
return Integer.MIN_VALUE;
}
private Map<String, Object> parseXml2Map(String xml) throws IOException {
xml = xml.replaceAll("\\r", "").replaceAll("\\n", "").replaceAll("\\t", "");
/**
* Returns the file extensions that the loader supports (excluding the '.').
* @return the file extensions
*/
@Override
public String[] getFileExtensions() {
return new String[] { "xml" };
}
/**
* 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(Resource resource) throws IOException {
Map<String, Object> map = new LinkedHashMap<>(32);
try {
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance()
.newDocumentBuilder();
Document document = documentBuilder
.parse(new InputSource(new StringReader(xml)));
Document document = documentBuilder.parse(resource.getInputStream());
if (null == document) {
return null;
}
@@ -89,7 +127,8 @@ public class NacosDataXmlParser extends AbstractNacosDataParser {
continue;
}
String key = StringUtils.isEmpty(parentKey) ? name : parentKey + DOT + name;
String key = StringUtils.isEmpty(parentKey) ? name
: parentKey + NacosDataParserHandler.DOT + name;
NamedNodeMap nodeMap = node.getAttributes();
parseNodeAttr(nodeMap, map, key);
if (node.getNodeType() == Node.ELEMENT_NODE && node.hasChildNodes()) {
@@ -120,8 +159,8 @@ public class NacosDataXmlParser extends AbstractNacosDataParser {
if (StringUtils.isEmpty(node.getNodeValue())) {
continue;
}
map.put(String.join(DOT, parentKey, node.getNodeName()),
node.getNodeValue());
map.put(String.join(NacosDataParserHandler.DOT, parentKey,
node.getNodeName()), node.getNodeValue());
}
}
}
@@ -4,4 +4,7 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.NacosConfigAutoConfiguration,\
com.alibaba.cloud.nacos.endpoint.NacosConfigEndpointAutoConfiguration
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureAnalyzer
com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureAnalyzer
org.springframework.boot.env.PropertySourceLoader=\
com.alibaba.cloud.nacos.parser.NacosJsonPropertySourceLoader,\
com.alibaba.cloud.nacos.parser.NacosXmlPropertySourceLoader
@@ -50,7 +50,8 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
*/
@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)
@PrepareForTest({ NacosConfigService.class })
@SpringBootTest(classes = NacosConfigurationNoSuffixTest.TestConfig.class, properties = {
@@ -49,7 +49,8 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
* @author zkz
*/
@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)
@PrepareForTest({ NacosConfigService.class })
@SpringBootTest(classes = NacosConfigurationXmlJsonTest.TestConfig.class, properties = {
@@ -83,13 +84,15 @@ public class NacosConfigurationXmlJsonTest {
throws Throwable {
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"
+ " <third>three</third>\n" + " </sencond>\n"
+ "</top>";
}
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"
+ " <Connector value=\"第二个连接器\"> \n"
+ " <open>开启服务</open> \n"
@@ -108,7 +111,8 @@ public class NacosConfigurationXmlJsonTest {
if ("test-name-dev.xml".equals(args[0])
&& "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"
+ " android:label=\"@string/app_name\" android:screenOrientation=\"landscape\">\n"
+ " <intent-filter>\n"
@@ -183,17 +183,17 @@ public class NacosDiscoveryProperties {
private String secretKey;
/**
* Heart beat interval. Time unit: second.
* Heart beat interval. Time unit: millisecond.
*/
private Integer heartBeatInterval;
/**
* Heart beat timeout. Time unit: second.
* Heart beat timeout. Time unit: millisecond.
*/
private Integer heartBeatTimeout;
/**
* Ip delete timeout. Time unit: second.
* Ip delete timeout. Time unit: millisecond.
*/
private Integer ipDeleteTimeout;
@@ -32,7 +32,7 @@ import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
/**
* @author xiaojing
@@ -59,7 +59,7 @@ public class NacosDiscoveryClientConfiguration {
matchIfMissing = true)
public NacosWatch nacosWatch(NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties,
ObjectProvider<TaskScheduler> taskExecutorObjectProvider) {
ObjectProvider<ThreadPoolTaskScheduler> taskExecutorObjectProvider) {
return new NacosWatch(nacosServiceManager, nacosDiscoveryProperties,
taskExecutorObjectProvider);
}
@@ -27,7 +27,6 @@ import java.util.concurrent.atomic.AtomicLong;
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.listener.Event;
import com.alibaba.nacos.api.naming.listener.EventListener;
@@ -41,7 +40,6 @@ import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.SmartLifecycle;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
/**
@@ -66,14 +64,15 @@ public class NacosWatch implements ApplicationEventPublisherAware, SmartLifecycl
private final NacosDiscoveryProperties properties;
private final TaskScheduler taskScheduler;
private final ThreadPoolTaskScheduler taskScheduler;
public NacosWatch(NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties properties,
ObjectProvider<TaskScheduler> taskScheduler) {
ObjectProvider<ThreadPoolTaskScheduler> taskScheduler) {
this.nacosServiceManager = nacosServiceManager;
this.properties = properties;
this.taskScheduler = taskScheduler.getIfAvailable(NacosWatch::getTaskScheduler);
this.taskScheduler = taskScheduler.stream().findAny()
.orElseGet(NacosWatch::getTaskScheduler);
}
private static ThreadPoolTaskScheduler getTaskScheduler() {
@@ -156,7 +155,7 @@ public class NacosWatch implements ApplicationEventPublisherAware, SmartLifecycl
if (this.watchFuture != null) {
// shutdown current user-thread,
// then the other daemon-threads will terminate automatic.
((ThreadPoolTaskScheduler) this.taskScheduler).shutdown();
this.taskScheduler.shutdown();
this.watchFuture.cancel(true);
}
@@ -167,7 +166,7 @@ public class NacosWatch implements ApplicationEventPublisherAware, SmartLifecycl
namingService.unsubscribe(properties.getService(), properties.getGroup(),
Arrays.asList(properties.getClusterName()), eventListener);
}
catch (NacosException e) {
catch (Exception e) {
log.error("namingService unsubscribe failed, properties:{}", properties,
e);
}
@@ -16,6 +16,7 @@
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.NacosDiscoveryClientConfiguration;
import com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration;
@@ -36,7 +37,7 @@ import org.springframework.context.annotation.Configuration;
matchIfMissing = false)
@Configuration(proxyBeanMethods = false)
@ImportAutoConfiguration({ NacosDiscoveryAutoConfiguration.class,
NacosDiscoveryClientConfiguration.class,
NacosServiceAutoConfiguration.class, NacosDiscoveryClientConfiguration.class,
NacosReactiveDiscoveryClientConfiguration.class })
public class NacosDiscoveryClientConfigServiceBootstrapConfiguration {
@@ -36,7 +36,7 @@ import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
*
* @author xiaojing
*/
@Endpoint(id = "nacos-discovery")
@Endpoint(id = "nacosdiscovery")
public class NacosDiscoveryEndpoint {
private static final Logger log = LoggerFactory
@@ -41,6 +41,10 @@ import static org.springframework.util.ReflectionUtils.rethrowRuntimeException;
*/
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 final NacosDiscoveryProperties nacosDiscoveryProperties;
@@ -119,7 +123,8 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> {
@Override
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);
return;
}
@@ -128,7 +133,7 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> {
Instance instance = getNacosInstanceFromRegistration(registration);
if (status.equalsIgnoreCase("DOWN")) {
if (STATUS_DOWN.equalsIgnoreCase(status)) {
instance.setEnabled(false);
}
else {
@@ -137,8 +142,8 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> {
try {
Properties nacosProperties = nacosDiscoveryProperties.getNacosProperties();
nacosServiceManager.getNamingMaintainService(nacosProperties)
.updateInstance(serviceId, instance);
nacosServiceManager.getNamingMaintainService(nacosProperties).updateInstance(
serviceId, nacosDiscoveryProperties.getGroup(), instance);
}
catch (Exception e) {
throw new RuntimeException("update nacos instance status fail", e);
@@ -28,7 +28,7 @@ import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
import com.netflix.loadbalancer.Server;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -69,9 +69,9 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
"spring.cloud.nacos.discovery.accessKey=test-accessKey",
"spring.cloud.nacos.discovery.ip=8.8.8.8",
"spring.cloud.nacos.discovery.secretKey=test-secretKey",
"spring.cloud.nacos.discovery.heart-beat-interval=3",
"spring.cloud.nacos.discovery.heart-beat-timeout=6",
"spring.cloud.nacos.discovery.ip-delete-timeout=9" },
"spring.cloud.nacos.discovery.heart-beat-interval=3000",
"spring.cloud.nacos.discovery.heart-beat-timeout=6000",
"spring.cloud.nacos.discovery.ip-delete-timeout=9000" },
webEnvironment = RANDOM_PORT)
public class NacosAutoServiceRegistrationTests {
@@ -186,15 +186,15 @@ public class NacosAutoServiceRegistrationTests {
}
private void checkoutNacosDiscoveryHeartBeatInterval() {
assertThat(properties.getHeartBeatInterval()).isEqualTo(Integer.valueOf(3));
assertThat(properties.getHeartBeatInterval()).isEqualTo(Integer.valueOf(3000));
}
private void checkoutNacosDiscoveryHeartBeatTimeout() {
assertThat(properties.getHeartBeatTimeout()).isEqualTo(Integer.valueOf(6));
assertThat(properties.getHeartBeatTimeout()).isEqualTo(Integer.valueOf(6000));
}
private void checkoutNacosDiscoveryIpDeleteTimeout() {
assertThat(properties.getIpDeleteTimeout()).isEqualTo(Integer.valueOf(9));
assertThat(properties.getIpDeleteTimeout()).isEqualTo(Integer.valueOf(9000));
}
private void checkoutNacosDiscoveryServiceName() {
@@ -36,6 +36,7 @@ import io.seata.core.context.RootContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
@@ -156,11 +157,15 @@ public class SeataHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy
public K call() throws Exception {
try {
RequestContextHolder.setRequestAttributes(requestAttributes);
RootContext.bind(xid);
if (!StringUtils.isEmpty(xid)) {
RootContext.bind(xid);
}
return actual.call();
}
finally {
RootContext.unbind();
if (!StringUtils.isEmpty(xid)) {
RootContext.unbind();
}
RequestContextHolder.resetRequestAttributes();
}
}
@@ -17,18 +17,25 @@
package com.alibaba.cloud.dubbo.autoconfigure;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.alibaba.cloud.dubbo.autoconfigure.condition.MissingSpringCloudRegistryConfigPropertyCondition;
import com.alibaba.cloud.dubbo.bootstrap.DubboBootstrapStartCommandLineRunner;
import com.alibaba.cloud.dubbo.bootstrap.DubboBootstrapWrapper;
import com.alibaba.cloud.dubbo.bootstrap.event.DubboBootstrapStartedEvent;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.registry.DubboServiceRegistrationEventPublishingAspect;
import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreDeregisteredEvent;
import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreRegisteredEvent;
import com.ecwid.consul.v1.agent.model.NewService;
import com.netflix.appinfo.InstanceInfo;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
import org.apache.dubbo.config.spring.ServiceBean;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -42,7 +49,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.cloud.consul.serviceregistry.ConsulRegistration;
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaAutoServiceRegistration;
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration;
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaServiceRegistry;
@@ -63,9 +72,11 @@ import static org.springframework.util.ObjectUtils.isEmpty;
* Dubbo Service Registration Auto-{@link Configuration}.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @author <a href="mailto:chenxilzx1@gmail.com">theonefx</a>
*/
@Configuration(proxyBeanMethods = false)
@Import({ DubboServiceRegistrationEventPublishingAspect.class })
@Import({ DubboServiceRegistrationEventPublishingAspect.class,
DubboBootstrapStartCommandLineRunner.class })
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled",
matchIfMissing = true)
@AutoConfigureAfter(name = { EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME,
@@ -106,10 +117,42 @@ public class DubboServiceRegistrationAutoConfiguration {
return new RegistryConfig(ADDRESS, PROTOCOL);
}
private Map<ServiceRegistry<Registration>, Set<Registration>> registrations = new ConcurrentHashMap<>();
@EventListener(DubboBootstrapStartedEvent.class)
public void onDubboBootstrapStarted(DubboBootstrapStartedEvent event) {
if (!event.getSource().isReady()) {
return;
}
registrations.forEach(
(registry, registrations) -> registrations.forEach(registration -> {
attachDubboMetadataServiceMetadata(registration);
registry.register(registration);
}));
}
@EventListener(ServiceInstancePreRegisteredEvent.class)
public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) {
Registration registration = event.getSource();
attachDubboMetadataServiceMetadata(registration);
if (!DubboBootstrap.getInstance().isReady()
|| !DubboBootstrap.getInstance().isStarted()) {
ServiceRegistry<Registration> registry = event.getRegistry();
synchronized (registry) {
registrations.putIfAbsent(registry, new HashSet<>());
registrations.get(registry).add(registration);
}
}
else {
attachDubboMetadataServiceMetadata(registration);
}
}
@EventListener(ServiceInstancePreDeregisteredEvent.class)
public void onServiceInstancePreDeregistered(
ServiceInstancePreDeregisteredEvent event) {
ServiceRegistry<Registration> registry = event.getRegistry();
registrations.remove(registry);
}
private void attachDubboMetadataServiceMetadata(Registration registration) {
@@ -132,21 +175,67 @@ public class DubboServiceRegistrationAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(name = EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME)
@Aspect
class EurekaConfiguration implements SmartInitializingSingleton {
@Autowired
private ObjectProvider<Collection<ServiceBean>> serviceBeans;
@EventListener(DubboBootstrapStartedEvent.class)
public void onDubboBootstrapStarted(DubboBootstrapStartedEvent event) {
DubboBootstrapWrapper wrapper = event.getSource();
if (!wrapper.isReady()) {
return;
}
registrations.forEach(
(registry, registrations) -> registrations.removeIf(registration -> {
if (!(registration instanceof EurekaRegistration)) {
return false;
}
EurekaRegistration eurekaRegistration = (EurekaRegistration) registration;
InstanceInfo instanceInfo = eurekaRegistration
.getApplicationInfoManager().getInfo();
EurekaInstanceConfigBean config = (EurekaInstanceConfigBean) eurekaRegistration
.getInstanceConfig();
config.setInitialStatus(InstanceInfo.InstanceStatus.UP);
attachDubboMetadataServiceMetadata(instanceInfo.getMetadata());
eurekaRegistration.getApplicationInfoManager()
.registerAppMetadata(instanceInfo.getMetadata());
eurekaRegistration.getApplicationInfoManager()
.setInstanceStatus(InstanceInfo.InstanceStatus.UP);
return true;
}));
}
@EventListener(ServiceInstancePreRegisteredEvent.class)
public void onServiceInstancePreRegistered(
ServiceInstancePreRegisteredEvent event) {
Registration registration = event.getSource();
EurekaRegistration eurekaRegistration = EurekaRegistration.class
.cast(registration);
InstanceInfo instanceInfo = eurekaRegistration.getApplicationInfoManager()
.getInfo();
attachDubboMetadataServiceMetadata(instanceInfo.getMetadata());
if (!(registration instanceof EurekaRegistration)) {
return;
}
if (DubboBootstrap.getInstance().isReady()
&& DubboBootstrap.getInstance().isStarted()) {
EurekaRegistration eurekaRegistration = (EurekaRegistration) registration;
InstanceInfo instanceInfo = eurekaRegistration.getApplicationInfoManager()
.getInfo();
EurekaInstanceConfigBean config = (EurekaInstanceConfigBean) eurekaRegistration
.getInstanceConfig();
config.setInitialStatus(InstanceInfo.InstanceStatus.UP);
attachDubboMetadataServiceMetadata(instanceInfo.getMetadata());
eurekaRegistration.getApplicationInfoManager()
.registerAppMetadata(instanceInfo.getMetadata());
}
else {
EurekaRegistration eurekaRegistration = (EurekaRegistration) registration;
EurekaInstanceConfigBean config = (EurekaInstanceConfigBean) eurekaRegistration
.getInstanceConfig();
config.setInitialStatus(InstanceInfo.InstanceStatus.STARTING);
}
}
/**
@@ -171,21 +260,28 @@ public class DubboServiceRegistrationAutoConfiguration {
@AutoConfigureOrder
class ConsulConfiguration {
/**
* Handle the pre-registered event of {@link ServiceInstance} for Consul.
* @param event {@link ServiceInstancePreRegisteredEvent}
*/
@EventListener(ServiceInstancePreRegisteredEvent.class)
public void onServiceInstancePreRegistered(
ServiceInstancePreRegisteredEvent event) {
Registration registration = event.getSource();
Class<?> registrationClass = AopUtils.getTargetClass(registration);
String registrationClassName = registrationClass.getName();
if (CONSUL_AUTO_SERVICE_AUTO_REGISTRATION_CLASS_NAME
.equalsIgnoreCase(registrationClassName)) {
ConsulRegistration consulRegistration = (ConsulRegistration) registration;
attachURLsIntoMetadata(consulRegistration);
@EventListener(DubboBootstrapStartedEvent.class)
public void attachURLsIntoMetadataBeforeReRegist(
DubboBootstrapStartedEvent event) {
if (!event.getSource().isReady()) {
return;
}
registrations.entrySet().removeIf(entry -> {
Set<Registration> registrations = entry.getValue();
registrations.removeIf(registration -> {
Class<?> registrationClass = AopUtils.getTargetClass(registration);
String registrationClassName = registrationClass.getName();
return !CONSUL_AUTO_SERVICE_AUTO_REGISTRATION_CLASS_NAME
.equalsIgnoreCase(registrationClassName);
});
return registrations.isEmpty();
});
registrations.forEach(
(registry, registrations) -> registrations.forEach(registration -> {
ConsulRegistration consulRegistration = (ConsulRegistration) registration;
attachURLsIntoMetadata(consulRegistration);
}));
}
private void attachURLsIntoMetadata(ConsulRegistration consulRegistration) {
@@ -0,0 +1,50 @@
/*
* 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.dubbo.bootstrap;
import com.alibaba.cloud.dubbo.bootstrap.event.DubboBootstrapStartedEvent;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
/**
* publish Dubbo microsystem startup finish event.
*
* @author <a href="mailto:chenxilzx1@gmail.com">theonefx</a>
*/
@Component
public class DubboBootstrapStartCommandLineRunner
implements CommandLineRunner, ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
@Override
public void run(String... args) {
applicationEventPublisher.publishEvent(
new DubboBootstrapStartedEvent(DubboBootstrapWrapper.getInstance()));
}
}
@@ -0,0 +1,47 @@
/*
* 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.dubbo.bootstrap;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
/**
* Wrapper DubboBootstrap operation.
*
* @author <a href="mailto:chenxilzx1@gmail.com">theonefx</a>
*/
public final class DubboBootstrapWrapper {
private DubboBootstrapWrapper() {
}
private static final DubboBootstrapWrapper INSTANCE = new DubboBootstrapWrapper();
public static DubboBootstrapWrapper getInstance() {
return INSTANCE;
}
public boolean isReady() {
return DubboBootstrap.getInstance().isStarted()
&& DubboBootstrap.getInstance().isReady()
&& DubboBootstrap.getInstance().isInitialized();
}
public DubboBootstrap getDubboBootstrap() {
return DubboBootstrap.getInstance();
}
}
@@ -0,0 +1,44 @@
/*
* 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.dubbo.bootstrap.event;
import com.alibaba.cloud.dubbo.bootstrap.DubboBootstrapWrapper;
import org.springframework.context.ApplicationEvent;
/**
* Dubbo microsytem start finish event, every thing is ready.
*
* @author <a href="mailto:chenxilzx1@gmail.com">theonefx</a>
*/
public class DubboBootstrapStartedEvent extends ApplicationEvent {
/**
* Create a new {@code DubboBootstrapStartedEvent}.
* @param source the object on which the event initially occurred or with which the
* event is associated (never {@code null})
*/
public DubboBootstrapStartedEvent(DubboBootstrapWrapper source) {
super(source);
}
@Override
public DubboBootstrapWrapper getSource() {
return (DubboBootstrapWrapper) super.getSource();
}
}
@@ -40,6 +40,7 @@ public abstract class AbstractHttpRequestMatcher implements HttpRequestMatcher {
* <p>
* For example {@code " || "} for URL patterns or {@code " && "} for param
* expressions.
* @return str
*/
protected abstract String getToStringInfix();
@@ -77,7 +77,7 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
/**
* Caches the IDs of {@link ApplicationListener}.
*/
private static final Set<String> registerListeners = new HashSet<>();
private static final Set<String> REGISTER_LISTENERS = new HashSet<>();
protected final Logger logger = LoggerFactory.getLogger(getClass());
@@ -193,7 +193,7 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
private void registerServiceInstancesChangedEventListener(URL url,
NotifyListener listener) {
String listenerId = generateId(url);
if (registerListeners.add(listenerId)) {
if (REGISTER_LISTENERS.add(listenerId)) {
applicationContext.addApplicationListener(
new ApplicationListener<ServiceInstancesChangedEvent>() {
@Override
@@ -261,7 +261,7 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
String listenerId = generateId(url);
// The metaservice will restart the new listener. It needs to be optimized
// to see whether the original listener can be reused.
this.registerListeners.remove(listenerId);
REGISTER_LISTENERS.remove(listenerId);
}
/**
@@ -83,7 +83,7 @@ public class DubboCloudRegistry extends FailbackRegistry {
/**
* Caches the IDs of {@link ApplicationListener}.
*/
private static final Set<String> registerListeners = new HashSet<>();
private static final Set<String> REGISTER_LISTENERS = new HashSet<>();
protected final Logger logger = LoggerFactory.getLogger(getClass());
@@ -215,7 +215,7 @@ public class DubboCloudRegistry extends FailbackRegistry {
private void registerServiceInstancesChangedListener(URL url,
ApplicationListener<ServiceInstancesChangedEvent> listener) {
String listenerId = generateId(url);
if (registerListeners.add(listenerId)) {
if (REGISTER_LISTENERS.add(listenerId)) {
applicationContext.addApplicationListener(listener);
}
}
@@ -290,7 +290,7 @@ public class DubboCloudRegistry extends FailbackRegistry {
.map(templateURL -> templateURL.removeParameter(PID_KEY))
.map(templateURL -> {
String protocol = templateURL.getProtocol();
int port = repository.getDubboProtocolPort(serviceInstance,
Integer port = repository.getDubboProtocolPort(serviceInstance,
protocol);
if (Objects.equals(templateURL.getHost(), host)
&& Objects.equals(templateURL.getPort(), port)) { // use
@@ -300,15 +300,27 @@ public class DubboCloudRegistry extends FailbackRegistry {
return templateURL;
}
URLBuilder clonedURLBuilder = from(templateURL) // remove the
// parameters from
// the template
// URL
.setHost(host) // reset the host
.setPort(port); // reset the port
if (port == null) {
if (logger.isWarnEnabled()) {
logger.warn(
"The protocol[{}] port of Dubbo service instance[host : {}] "
+ "can't be resolved",
protocol, host);
}
return null;
}
else {
URLBuilder clonedURLBuilder = from(templateURL) // remove the
// parameters from
// the template
// URL
.setHost(host) // reset the host
.setPort(port); // reset the port
return clonedURLBuilder.build();
}).forEach(clonedExportedURLs::add);
return clonedURLBuilder.build();
}
}).filter(Objects::nonNull).forEach(clonedExportedURLs::add);
});
return clonedExportedURLs;
}
@@ -16,6 +16,7 @@
package com.alibaba.cloud.dubbo.registry;
import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreDeregisteredEvent;
import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreRegisteredEvent;
import com.alibaba.cloud.dubbo.registry.event.ServiceInstanceRegisteredEvent;
import org.aspectj.lang.annotation.After;
@@ -33,6 +34,7 @@ import org.springframework.context.ApplicationEventPublisherAware;
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @see ServiceInstancePreRegisteredEvent
* @see ServiceInstanceRegisteredEvent
* @see ServiceInstancePreDeregisteredEvent
*/
@Aspect
public class DubboServiceRegistrationEventPublishingAspect
@@ -41,18 +43,29 @@ public class DubboServiceRegistrationEventPublishingAspect
/**
* The pointcut expression for {@link ServiceRegistry#register(Registration)}.
*/
public static final String REGISTER_POINTCUT_EXPRESSION = "execution(* org.springframework.cloud.client.serviceregistry.ServiceRegistry.register(*)) && args(registration)";
public static final String REGISTER_POINTCUT_EXPRESSION = "execution(* org.springframework.cloud.client.serviceregistry.ServiceRegistry.register(*)) && target(registry) && args(registration)";
/**
* The pointcut expression for {@link ServiceRegistry#deregister(Registration)}.
*/
public static final String DEREGISTER_POINTCUT_EXPRESSION = "execution(* org.springframework.cloud.client.serviceregistry.ServiceRegistry.deregister(*)) && target(registry) && args(registration)";
private ApplicationEventPublisher applicationEventPublisher;
@Before(REGISTER_POINTCUT_EXPRESSION)
public void beforeRegister(Registration registration) {
applicationEventPublisher
.publishEvent(new ServiceInstancePreRegisteredEvent(registration));
@Before(value = REGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration")
public void beforeRegister(ServiceRegistry registry, Registration registration) {
applicationEventPublisher.publishEvent(
new ServiceInstancePreRegisteredEvent(registry, registration));
}
@After(REGISTER_POINTCUT_EXPRESSION)
public void afterRegister(Registration registration) {
@Before(value = DEREGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration")
public void beforeDeregister(ServiceRegistry registry, Registration registration) {
applicationEventPublisher.publishEvent(
new ServiceInstancePreDeregisteredEvent(registry, registration));
}
@After(value = REGISTER_POINTCUT_EXPRESSION, argNames = "registry, registration")
public void afterRegister(ServiceRegistry registry, Registration registration) {
applicationEventPublisher
.publishEvent(new ServiceInstanceRegisteredEvent(registration));
}
@@ -0,0 +1,49 @@
/*
* 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.dubbo.registry.event;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.ApplicationEvent;
/**
* The
* before-{@link org.springframework.cloud.client.serviceregistry.ServiceRegistry#register(org.springframework.cloud.client.serviceregistry.Registration)
* register} event for {@link org.springframework.cloud.client.ServiceInstance}.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class ServiceInstancePreDeregisteredEvent extends ApplicationEvent {
private final ServiceRegistry<Registration> registry;
public ServiceInstancePreDeregisteredEvent(ServiceRegistry<Registration> registry,
Registration source) {
super(source);
this.registry = registry;
}
@Override
public Registration getSource() {
return (Registration) super.getSource();
}
public ServiceRegistry<Registration> getRegistry() {
return registry;
}
}
@@ -29,8 +29,12 @@ import org.springframework.context.ApplicationEvent;
*/
public class ServiceInstancePreRegisteredEvent extends ApplicationEvent {
public ServiceInstancePreRegisteredEvent(Registration source) {
private final ServiceRegistry<Registration> registry;
public ServiceInstancePreRegisteredEvent(ServiceRegistry<Registration> registry,
Registration source) {
super(source);
this.registry = registry;
}
@Override
@@ -38,4 +42,8 @@ public class ServiceInstancePreRegisteredEvent extends ApplicationEvent {
return (Registration) super.getSource();
}
public ServiceRegistry<Registration> getRegistry() {
return registry;
}
}