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
pull/1531/head
zkzlx 4 years ago
commit 359b3ea522

@ -39,8 +39,6 @@ Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。
**[Seata](https://github.com/seata/seata)**:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
**[Alibaba Cloud ACM](https://www.aliyun.com/product/acm)**:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。
**[Alibaba Cloud OSS](https://www.aliyun.com/product/oss)**: 阿里云对象存储服务Object Storage Service简称 OSS是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
**[Alibaba Cloud SchedulerX](https://help.aliyun.com/document_detail/43136.html)**: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
@ -72,7 +70,7 @@ Spring Cloud 使用 Maven 来构建,最快的使用方式是将本项目 clone
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.0.RELEASE</version>
<version>2.2.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@ -97,13 +95,11 @@ Example 列表:
[Seata Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/seata-example/readme-zh.md)
[Alibaba Cloud OSS Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/oss-example/readme-zh.md)
[Alibaba Cloud ANS Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/ans-example/ans-provider-example/readme-zh.md)
[Alibaba Cloud OSS Example](https://github.com/alibaba/aliyun-spring-boot/tree/master/aliyun-spring-boot-samples/aliyun-oss-spring-boot-sample)
[Alibaba Cloud ACM Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/acm-example/acm-local-example/readme-zh.md)
[Alibaba Cloud SMS Example](https://github.com/alibaba/aliyun-spring-boot/tree/master/aliyun-spring-boot-samples/aliyun-sms-spring-boot-sample)
[Alibaba Cloud SchedulerX Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/schedulerx-example/schedulerx-simple-task-example/readme-zh.md)
[Alibaba Cloud SchedulerX Example](https://github.com/alibaba/aliyun-spring-boot)
## 版本管理规范

@ -45,7 +45,7 @@ For more features, please refer to [Roadmap](https://github.com/alibaba/spring-c
**[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/product/SchedulerX)**:accurate, highly reliable, and highly available scheduled job scheduling services with response time within seconds..
**[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).
@ -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.1.RELEASE</version>
<version>2.2.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@ -89,8 +89,6 @@ add the module in `dependencies`.
[Nacos Discovery](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-discovery.adoc)
[ACM](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/acm.adoc)
## Examples
@ -106,7 +104,7 @@ Examples
[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/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/oss-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)

@ -8,7 +8,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>2.2.3.RELEASE</version>
<version>2.3.1.RELEASE</version>
<relativePath/>
</parent>
@ -80,25 +80,21 @@
<properties>
<!-- Project revision -->
<revision>2.2.1.RELEASE</revision>
<revision>2.2.3-SNAPSHOT</revision>
<!-- Dependency Versions -->
<spring-cloud-commons.version>2.2.2.RELEASE</spring-cloud-commons.version>
<spring-cloud-netflix.version>2.2.2.RELEASE</spring-cloud-netflix.version>
<spring-cloud-openfeign.version>2.2.2.RELEASE</spring-cloud-openfeign.version>
<spring-cloud-bus.version>2.2.1.RELEASE</spring-cloud-bus.version>
<spring-cloud-gateway.version>2.2.2.RELEASE</spring-cloud-gateway.version>
<spring-cloud-commons.version>2.2.5.RELEASE</spring-cloud-commons.version>
<spring-cloud-netflix.version>2.2.5.RELEASE</spring-cloud-netflix.version>
<spring-cloud-openfeign.version>2.2.5.RELEASE</spring-cloud-openfeign.version>
<spring-cloud-bus.version>2.2.3.RELEASE</spring-cloud-bus.version>
<spring-cloud-gateway.version>2.2.5.RELEASE</spring-cloud-gateway.version>
<spring-cloud-stream.version>Horsham.SR3</spring-cloud-stream.version>
<spring-cloud-consul.version>2.2.2.RELEASE</spring-cloud-consul.version>
<spring-cloud-config.version>2.2.2.RELEASE</spring-cloud-config.version>
<spring-cloud-zookeeper.version>2.2.1.RELEASE</spring-cloud-zookeeper.version>
<junit.version>4.12</junit.version>
<javax-servlet-api>3.0</javax-servlet-api>
<slf4j-api.version>1.7.25</slf4j-api.version>
<spring-cloud-consul.version>2.2.4.RELEASE</spring-cloud-consul.version>
<spring-cloud-config.version>2.2.5.RELEASE</spring-cloud-config.version>
<spring-cloud-zookeeper.version>2.2.3.RELEASE</spring-cloud-zookeeper.version>
<!-- Apache Dubbo -->
<dubbo.version>2.7.6</dubbo.version>
<dubbo.version>2.7.8</dubbo.version>
<curator.version>4.0.1</curator.version>
<!-- Apache RocketMQ -->
@ -449,4 +445,4 @@
</profile>
</profiles>
</project>
</project>

@ -4,9 +4,9 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>2.2.0.RELEASE</version>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/>
</parent>
@ -18,13 +18,12 @@
<description>Spring Cloud Alibaba Dependencies</description>
<properties>
<revision>2.2.1.RELEASE</revision>
<sentinel.version>1.7.2</sentinel.version>
<oss.version>3.1.0</oss.version>
<seata.version>1.2.0</seata.version>
<nacos.client.version>1.2.1</nacos.client.version>
<revision>2.2.3.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.config.version>0.8.0</nacos.config.version>
<spring.context.support.version>1.0.6</spring.context.support.version>
<spring.context.support.version>1.0.10</spring.context.support.version>
<!-- Maven Plugin Versions -->
<maven-source-plugin.version>2.2.1</maven-source-plugin.version>

@ -10,7 +10,7 @@ Spring Cloud Alibaba BOM 包含了它所使用的所有依赖的版本。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.0.RELEASE</version>
<version>2.2.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>

@ -229,7 +229,7 @@ in develop-env enviroment; user name :nacos-config-yaml-update; age: 68
spring.profiles.active=product
----
同时生产环境上 Nacos 需要添加对应 DataId 的基础配置。例如,在生环境下的 Naocs 添加了DataId为nacos-config-product.yaml的配置
同时生产环境上 Nacos 需要添加对应 DataId 的基础配置。例如,在生环境下的 Naocs 添加了DataId为nacos-config-product.yaml的配置
[source,subs="normal"]
----
@ -415,8 +415,8 @@ Endpoint 暴露的 json 中包含了三种属性:
|===
|配置项 |Key |默认值 |说明
|服务端地址|`spring.cloud.nacos.config.server-addr`|| Nacos Server 启动监听的ip地址和端口
|配置对应的 DataId|`spring.cloud.nacos.config.name`|| 先取 prefix name最后取 spring.application.name
|配置对应的 DataId|`spring.cloud.nacos.config.prefix`|| 先取 prefix name最后取 spring.application.name
|配置对应的 DataId|`spring.cloud.nacos.config.name`|| 先取 prefix name最后取 spring.application.name
|配置对应的 DataId|`spring.cloud.nacos.config.prefix`|| 先取 prefix name最后取 spring.application.name
|配置内容编码|`spring.cloud.nacos.config.encode`||读取的配置内容对应的编码
|GROUP|`spring.cloud.nacos.config.group`|`DEFAULT_GROUP`|配置对应的组
|文件扩展名|`spring.cloud.nacos.config.fileExtension`|`properties`|配置项对应的文件扩展名,目前支持 properties 和 yaml(yml)

@ -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.0.RELEASE</version>
<version>2.2.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>

@ -25,6 +25,7 @@ Before we start the demo, let's learn how to connect Nacos Config to a Spring Cl
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
3. After completing the above two steps, the application will get the externalized configuration from Nacos Server and put it in the Spring Environment's PropertySources.We use the @Value annotation to inject the corresponding configuration into the userName and age fields of the SampleController, and add @RefreshScope to turn on dynamic refresh .
@RefreshScope
class SampleController {

@ -30,7 +30,7 @@
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
SpringApplication.run(ProviderApplication.class, args);
}
@RestController

@ -29,7 +29,7 @@ Before we start the demo, let's learn how to connect Nacos Config to a Spring Cl
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
SpringApplication.run(ProviderApplication.class, args);
}
@RestController
@ -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

@ -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>

@ -25,10 +25,10 @@ import org.springframework.web.client.RestTemplate;
* @author xiaojing
*/
@SpringBootApplication
public class OderApplication {
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OderApplication.class, args);
SpringApplication.run(OrderApplication.class, args);
}
@Bean

@ -17,7 +17,7 @@
package com.alibaba.cloud.dubbo.bootstrap;
import com.alibaba.cloud.dubbo.service.EchoService;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@ -27,13 +27,15 @@ import org.springframework.web.bind.annotation.RestController;
/**
* Dubbo Spring Cloud Client Bootstrap.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@EnableDiscoveryClient
@EnableAutoConfiguration
@RestController
public class DubboSpringCloudClientBootstrap {
@Reference
@DubboReference
private EchoService echoService;
@GetMapping("/echo")

@ -1,8 +1,9 @@
dubbo:
registry:
address: spring-cloud://localhost
cloud:
subscribed-services: spring-cloud-alibaba-dubbo-server
protocols:
dubbo:
port: -1
spring:
application:

@ -23,7 +23,7 @@ import com.alibaba.cloud.dubbo.annotation.DubboTransported;
import com.alibaba.cloud.dubbo.service.RestService;
import com.alibaba.cloud.dubbo.service.User;
import com.alibaba.cloud.dubbo.service.UserService;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@ -52,6 +52,8 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
/**
* Dubbo Spring Cloud Consumer Bootstrap.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@EnableDiscoveryClient
@EnableAutoConfiguration
@ -60,10 +62,10 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@EnableCaching
public class DubboSpringCloudConsumerBootstrap {
@Reference
@DubboReference
private UserService userService;
@Reference(version = "1.0.0", protocol = "dubbo")
@DubboReference(version = "1.0.0", protocol = "dubbo")
private RestService restService;
@Autowired

@ -1,15 +1,10 @@
dubbo:
registry:
# The Spring Cloud Dubbo's registry extension
## the default value of dubbo-provider-services is "*", that means to subscribe all providers,
## thus it's optimized if subscriber specifies the required providers.
address: spring-cloud://localhost
# The traditional Dubbo's registry also is supported
# address: zookeeper://127.0.0.1:2181
cloud:
# The subscribed services in consumer side
subscribed-services: ${provider.application.name}
protocols:
dubbo:
port: -1
consumer:
check: false

@ -23,6 +23,8 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* Dubbo Spring Cloud Provider Bootstrap.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@EnableDiscoveryClient
@EnableAutoConfiguration

@ -20,12 +20,12 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.config.annotation.DubboService;
/**
* In-Memory {@link UserService} implementation.
*/
@Service(protocol = "dubbo")
@DubboService(protocol = "dubbo")
public class InMemoryUserService implements UserService {
private Map<Long, User> usersRepository = new HashMap<>();

@ -30,7 +30,7 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -42,7 +42,7 @@ import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Service(version = "1.0.0", protocol = { "dubbo", "rest" })
@DubboService(version = "1.0.0", protocol = { "dubbo", "rest" })
@Path("/")
public class StandardRestService implements RestService {

@ -9,11 +9,6 @@ dubbo:
name: rest
port: 9090
server: netty
registry:
# The Spring Cloud Dubbo's registry extension
address: spring-cloud://localhost
# The traditional Dubbo's registry
# address: zookeeper://127.0.0.1:2181
feign:
hystrix:
enabled: true

@ -33,6 +33,7 @@ spring:
enabled: true
register-enabled: true
server-addr: 127.0.0.1:8848
ephemeral: false
---

@ -22,6 +22,8 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* Dubbo Spring Cloud Provider Bootstrap.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@EnableDiscoveryClient
@EnableAutoConfiguration

@ -20,12 +20,12 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.config.annotation.DubboService;
/**
* In-Memory {@link UserService} implementation.
*/
@Service(protocol = "dubbo")
@DubboService(protocol = "dubbo")
public class InMemoryUserService implements UserService {
private Map<Long, User> usersRepository = new HashMap<>();

@ -19,7 +19,7 @@ package com.alibaba.cloud.dubbo.service;
import java.util.HashMap;
import java.util.Map;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -39,7 +39,7 @@ import static com.alibaba.cloud.dubbo.util.LoggerUtils.log;
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Service(version = "1.0.0")
@DubboService(version = "1.0.0")
@RestController
public class SpringRestService implements RestService {

@ -3,16 +3,7 @@ dubbo:
base-packages: com.alibaba.cloud.dubbo.service
protocols:
dubbo:
name: dubbo
port: -1
registries:
new:
address: spring-cloud://localhost
# registry:
# The Spring Cloud Dubbo's registry extension
# address: spring-cloud://localhost
# The traditional Dubbo's registry
# address: nacos://127.0.0.1:8848
feign:
hystrix:

@ -17,7 +17,7 @@
package com.alibaba.cloud.dubbo.bootstrap;
import com.alibaba.cloud.dubbo.service.EchoService;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@ -25,6 +25,8 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* Dubbo Spring Cloud Server Bootstrap.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@EnableDiscoveryClient
@EnableAutoConfiguration
@ -36,7 +38,7 @@ public class DubboSpringCloudServerBootstrap {
}
@Service
@DubboService
class EchoServiceImpl implements EchoService {
@Override

@ -4,8 +4,6 @@ dubbo:
protocol:
name: dubbo
port: -1
registry:
address: spring-cloud://localhost
spring:
application:

@ -24,6 +24,8 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* Dubbo Spring Cloud Servlet Gateway Bootstrap.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@EnableDiscoveryClient
@EnableAutoConfiguration

@ -93,7 +93,7 @@ public class RedisDataSourceProperties extends AbstractDataSourceProperties {
"RedisDataSource channel can not be empty");
}
if (!StringUtils.isEmpty(masterId) && StringUtils.isEmpty(masterId)) {
if (StringUtils.isEmpty(masterId)) {
throw new IllegalArgumentException(
"RedisDataSource sentinel modelmasterId can not be empty");
}

@ -67,8 +67,12 @@ public class NacosDataSourceFactoryBean implements FactoryBean<NacosDataSource>
if (!StringUtils.isEmpty(this.namespace)) {
properties.setProperty(PropertyKeyConst.NAMESPACE, this.namespace);
}
properties.setProperty(PropertyKeyConst.USERNAME, this.username);
properties.setProperty(PropertyKeyConst.PASSWORD, this.password);
if (!StringUtils.isEmpty(this.username)) {
properties.setProperty(PropertyKeyConst.USERNAME, this.username);
}
if (!StringUtils.isEmpty(this.password)) {
properties.setProperty(PropertyKeyConst.PASSWORD, this.password);
}
return new NacosDataSource(properties, groupId, dataId, converter);
}

@ -59,6 +59,7 @@
<artifactId>spring-cloud-starter-gateway</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>

@ -91,7 +91,8 @@ public class SentinelSCGAutoConfiguration {
}
private void initAppType() {
System.setProperty(SentinelConfig.APP_TYPE, ConfigConstants.APP_TYPE_SCG_GATEWAY);
System.setProperty(SentinelConfig.APP_TYPE_PROP_KEY,
ConfigConstants.APP_TYPE_SCG_GATEWAY);
}
private void initFallback() {

@ -65,7 +65,7 @@ public class SentinelZuulAutoConfiguration {
private void init() {
requestOriginParserOptional
.ifPresent(ZuulGatewayCallbackManager::setOriginParser);
System.setProperty(SentinelConfig.APP_TYPE,
System.setProperty(SentinelConfig.APP_TYPE_PROP_KEY,
String.valueOf(ConfigConstants.APP_TYPE_ZUUL_GATEWAY));
}

@ -82,7 +82,7 @@ public class ReactiveSentinelCircuitBreakerIntegrationTest {
Thread.sleep(1000);
}
// Recovered.
// Half-open recovery (will re-open the circuit breaker).
StepVerifier.create(service.slow()).expectNext("slow").verifyComplete();
StepVerifier.create(service.normalFlux()).expectNext("normalflux")
@ -99,7 +99,7 @@ public class ReactiveSentinelCircuitBreakerIntegrationTest {
Thread.sleep(1000);
}
// Recovered.
// Half-open recovery (will re-open the circuit breaker).
StepVerifier.create(service.slowFlux()).expectNext("slowflux").verifyComplete();
}
@ -110,7 +110,7 @@ public class ReactiveSentinelCircuitBreakerIntegrationTest {
@GetMapping("/slow")
public Mono<String> slow() {
return Mono.just("slow").delayElement(Duration.ofMillis(500));
return Mono.just("slow").delayElement(Duration.ofMillis(80));
}
@GetMapping("/normal")
@ -120,7 +120,7 @@ public class ReactiveSentinelCircuitBreakerIntegrationTest {
@GetMapping("/slow_flux")
public Flux<String> slowFlux() {
return Flux.just("slow", "flux").delayElements(Duration.ofMillis(500));
return Flux.just("slow", "flux").delayElements(Duration.ofMillis(80));
}
@GetMapping("normal_flux")
@ -131,21 +131,23 @@ public class ReactiveSentinelCircuitBreakerIntegrationTest {
@Bean
public Customizer<ReactiveSentinelCircuitBreakerFactory> slowCustomizer() {
return factory -> {
factory.configure(builder -> builder
.rules(Collections.singletonList(new DegradeRule("slow_mono")
.setGrade(RuleConstant.DEGRADE_GRADE_RT).setCount(100)
.setTimeWindow(5))),
factory.configure(
builder -> builder.rules(Collections
.singletonList(new DegradeRule("slow_mono").setCount(50)
.setSlowRatioThreshold(0.7).setMinRequestAmount(5)
.setStatIntervalMs(30000).setTimeWindow(5))),
"slow_mono");
factory.configure(builder -> builder
.rules(Collections.singletonList(new DegradeRule("slow_flux")
.setGrade(RuleConstant.DEGRADE_GRADE_RT).setCount(100)
.setTimeWindow(5))),
factory.configure(
builder -> builder.rules(Collections
.singletonList(new DegradeRule("slow_mono").setCount(50)
.setSlowRatioThreshold(0.7).setMinRequestAmount(5)
.setStatIntervalMs(30000).setTimeWindow(5))),
"slow_flux");
factory.configureDefault(id -> new SentinelConfigBuilder()
.resourceName(id)
.rules(Collections.singletonList(new DegradeRule(id)
.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT)
.setCount(0.5).setTimeWindow(10)))
.setCount(5).setTimeWindow(10)))
.build());
};
}

@ -39,6 +39,7 @@ import org.springframework.stereotype.Service;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import static org.assertj.core.api.Assertions.assertThat;
@ -59,21 +60,24 @@ public class SentinelCircuitBreakerIntegrationTest {
@Test
public void testSlow() throws Exception {
// The first 5 requests should pass.
assertThat(service.slow()).isEqualTo("slow");
assertThat(service.slow()).isEqualTo("slow");
assertThat(service.slow()).isEqualTo("slow");
assertThat(service.slow()).isEqualTo("slow");
assertThat(service.slow()).isEqualTo("slow");
assertThat(service.slow(true)).isEqualTo("slow");
assertThat(service.slow(true)).isEqualTo("slow");
assertThat(service.slow(true)).isEqualTo("slow");
assertThat(service.slow(false)).isEqualTo("slow");
assertThat(service.slow(false)).isEqualTo("slow");
assertThat(service.slow(true)).isEqualTo("slow");
assertThat(service.slow(true)).isEqualTo("slow");
// Then in the next 10s, the fallback method should be called.
for (int i = 0; i < 10; i++) {
assertThat(service.slow()).isEqualTo("fallback");
for (int i = 0; i < 5; i++) {
assertThat(service.slow(true)).isEqualTo("fallback");
Thread.sleep(1000);
}
// Try a normal request.
assertThat(service.slow(false)).isEqualTo("slow");
// Recovered.
assertThat(service.slow()).isEqualTo("slow");
assertThat(service.slow(true)).isEqualTo("slow");
}
@Test
@ -97,8 +101,11 @@ public class SentinelCircuitBreakerIntegrationTest {
protected static class Application {
@GetMapping("/slow")
public String slow() throws InterruptedException {
Thread.sleep(500);
public String slow(@RequestParam(required = false) Boolean slow)
throws InterruptedException {
if (slow == null || slow) {
Thread.sleep(80);
}
return "slow";
}
@ -110,16 +117,17 @@ public class SentinelCircuitBreakerIntegrationTest {
@Bean
public Customizer<SentinelCircuitBreakerFactory> slowCustomizer() {
String slowId = "slow";
List<DegradeRule> rules = Collections.singletonList(
new DegradeRule(slowId).setGrade(RuleConstant.DEGRADE_GRADE_RT)
.setCount(100).setTimeWindow(10));
List<DegradeRule> rules = Collections.singletonList(new DegradeRule(slowId)
.setGrade(RuleConstant.DEGRADE_GRADE_RT).setCount(50)
.setSlowRatioThreshold(0.7).setMinRequestAmount(5)
.setStatIntervalMs(30000).setTimeWindow(5));
return factory -> {
factory.configure(builder -> builder.rules(rules), slowId);
factory.configureDefault(id -> new SentinelConfigBuilder()
.resourceName(id)
.rules(Collections.singletonList(new DegradeRule(id)
.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT)
.setCount(0.5).setTimeWindow(10)))
.setCount(10).setStatIntervalMs(10000).setTimeWindow(10)))
.build());
};
}
@ -137,9 +145,10 @@ public class SentinelCircuitBreakerIntegrationTest {
this.cbFactory = cbFactory;
}
public String slow() {
public String slow(boolean slow) {
return cbFactory.create("slow").run(
() -> rest.getForObject("/slow", String.class), t -> "fallback");
() -> rest.getForObject("/slow?slow=" + slow, String.class),
t -> "fallback");
}
public String normal() {

@ -22,7 +22,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@ -487,8 +486,7 @@ public class NacosConfigProperties {
List<Config> result = new ArrayList<>();
configList.stream()
.collect(Collectors.groupingBy(cfg -> (cfg.getGroup() + cfg.getDataId()),
() -> new ConcurrentHashMap<>(new LinkedHashMap<>()),
Collectors.toList()))
LinkedHashMap::new, Collectors.toList()))
.forEach((key, list) -> {
list.stream()
.reduce((a, b) -> new Config(a.getDataId(), a.getGroup(),

@ -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;

@ -20,7 +20,7 @@ import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.cloud.nacos.refresh.NacosRefreshHistory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@ -44,7 +44,7 @@ public class NacosConfigEndpointAutoConfiguration {
private NacosRefreshHistory nacosRefreshHistory;
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
@ConditionalOnAvailableEndpoint
@Bean
public NacosConfigEndpoint nacosConfigEndpoint() {
return new NacosConfigEndpoint(nacosConfigManager.getNacosConfigProperties(),

@ -19,7 +19,6 @@ package com.alibaba.cloud.nacos;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
@ -30,9 +29,7 @@ import java.util.regex.Pattern;
import javax.annotation.PostConstruct;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.naming.NamingMaintainFactory;
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.PreservedMetadataKeys;
import com.alibaba.nacos.client.naming.utils.UtilAndComs;
@ -44,6 +41,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
@ -204,19 +202,25 @@ public class NacosDiscoveryProperties {
*/
private boolean instanceEnabled = true;
/**
* If instance is ephemeral.The default value is true.
*/
private boolean ephemeral = true;
@Autowired
private InetUtils inetUtils;
@Autowired
private Environment environment;
private static NamingService namingService;
@Autowired
private NacosServiceManager nacosServiceManager;
private static NamingMaintainService namingMaintainService;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@PostConstruct
public void init() throws SocketException {
namingService = null;
public void init() throws Exception {
metadata.put(PreservedMetadataKeys.REGISTER_SOURCE, "SPRING_CLOUD");
if (secure) {
@ -263,6 +267,19 @@ public class NacosDiscoveryProperties {
}
this.overrideFromEnv(environment);
if (nacosServiceManager.isNacosDiscoveryInfoChanged(this)) {
applicationEventPublisher
.publishEvent(new NacosDiscoveryInfoChangedEvent(this));
}
}
/**
* recommend to use {@link NacosServiceManager#getNamingService(Properties)}.
* @return NamingService
*/
@Deprecated
public NamingService namingServiceInstance() {
return nacosServiceManager.getNamingService(this.getNacosProperties());
}
public String getEndpoint() {
@ -461,6 +478,50 @@ public class NacosDiscoveryProperties {
this.instanceEnabled = instanceEnabled;
}
public boolean isEphemeral() {
return ephemeral;
}
public void setEphemeral(boolean ephemeral) {
this.ephemeral = ephemeral;
}
@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(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);
}
@Override
public String toString() {
return "NacosDiscoveryProperties{" + "serverAddr='" + serverAddr + '\''
@ -524,41 +585,7 @@ public class NacosDiscoveryProperties {
}
}
public NamingService namingServiceInstance() {
if (null != namingService) {
return namingService;
}
try {
namingService = NacosFactory.createNamingService(getNacosProperties());
}
catch (Exception e) {
log.error("create naming service error!properties={},e=,", this, e);
return null;
}
return namingService;
}
@Deprecated
public NamingMaintainService namingMaintainServiceInstance() {
if (null != namingMaintainService) {
return namingMaintainService;
}
try {
namingMaintainService = NamingMaintainFactory
.createMaintainService(getNacosProperties());
}
catch (Exception e) {
log.error("create naming service error!properties={},e=,", this, e);
return null;
}
return namingMaintainService;
}
private Properties getNacosProperties() {
public Properties getNacosProperties() {
Properties properties = new Properties();
properties.put(SERVER_ADDR, serverAddr);
properties.put(USERNAME, Objects.toString(username, ""));

@ -0,0 +1,36 @@
/*
* 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();
}
}

@ -0,0 +1,134 @@
/*
* 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 {
this.namingService.shutDown();
namingService = null;
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;
import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
@ -41,8 +42,9 @@ public class NacosDiscoveryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public NacosServiceDiscovery nacosServiceDiscovery(
NacosDiscoveryProperties discoveryProperties) {
return new NacosServiceDiscovery(discoveryProperties);
NacosDiscoveryProperties discoveryProperties,
NacosServiceManager nacosServiceManager) {
return new NacosServiceDiscovery(discoveryProperties, nacosServiceManager);
}
}

@ -18,6 +18,7 @@ package com.alibaba.cloud.nacos.discovery;
import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
@ -56,9 +57,11 @@ public class NacosDiscoveryClientConfiguration {
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.nacos.discovery.watch.enabled",
matchIfMissing = true)
public NacosWatch nacosWatch(NacosDiscoveryProperties nacosDiscoveryProperties,
ObjectProvider<TaskScheduler> taskScheduler) {
return new NacosWatch(nacosDiscoveryProperties, taskScheduler);
public NacosWatch nacosWatch(NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties,
ObjectProvider<TaskScheduler> taskExecutorObjectProvider) {
return new NacosWatch(nacosServiceManager, nacosDiscoveryProperties,
taskExecutorObjectProvider);
}
}

@ -23,7 +23,9 @@ import java.util.Map;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceInstance;
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.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
@ -36,8 +38,12 @@ public class NacosServiceDiscovery {
private NacosDiscoveryProperties discoveryProperties;
public NacosServiceDiscovery(NacosDiscoveryProperties discoveryProperties) {
private NacosServiceManager nacosServiceManager;
public NacosServiceDiscovery(NacosDiscoveryProperties discoveryProperties,
NacosServiceManager nacosServiceManager) {
this.discoveryProperties = discoveryProperties;
this.nacosServiceManager = nacosServiceManager;
}
/**
@ -48,8 +54,8 @@ public class NacosServiceDiscovery {
*/
public List<ServiceInstance> getInstances(String serviceId) throws NacosException {
String group = discoveryProperties.getGroup();
List<Instance> instances = discoveryProperties.namingServiceInstance()
.selectInstances(serviceId, group, true);
List<Instance> instances = namingService().selectInstances(serviceId, group,
true);
return hostToServiceInstanceList(instances, serviceId);
}
@ -60,8 +66,8 @@ public class NacosServiceDiscovery {
*/
public List<String> getServices() throws NacosException {
String group = discoveryProperties.getGroup();
ListView<String> services = discoveryProperties.namingServiceInstance()
.getServicesOfServer(1, Integer.MAX_VALUE, group);
ListView<String> services = namingService().getServicesOfServer(1,
Integer.MAX_VALUE, group);
return services.getData();
}
@ -92,7 +98,10 @@ public class NacosServiceDiscovery {
metadata.put("nacos.weight", instance.getWeight() + "");
metadata.put("nacos.healthy", instance.isHealthy() + "");
metadata.put("nacos.cluster", instance.getClusterName() + "");
metadata.putAll(instance.getMetadata());
if (instance.getMetadata() != null) {
metadata.putAll(instance.getMetadata());
}
metadata.put("nacos.ephemeral", String.valueOf(instance.isEphemeral()));
nacosServiceInstance.setMetadata(metadata);
if (metadata.containsKey("secure")) {
@ -102,4 +111,9 @@ public class NacosServiceDiscovery {
return nacosServiceInstance;
}
private NamingService namingService() {
return nacosServiceManager
.getNamingService(discoveryProperties.getNacosProperties());
}
}

@ -16,11 +16,23 @@
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.atomic.AtomicBoolean;
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;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -34,47 +46,39 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
/**
* @author xiaojing
* @author yuhuangbin
*/
public class NacosWatch implements ApplicationEventPublisherAware, SmartLifecycle {
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 AtomicBoolean running = new AtomicBoolean(false);
private ApplicationEventPublisher publisher;
private ScheduledFuture<?> watchFuture;
public NacosWatch(NacosDiscoveryProperties properties) {
this(properties, getTaskScheduler());
}
private NacosServiceManager nacosServiceManager;
public NacosWatch(NacosDiscoveryProperties properties, TaskScheduler taskScheduler) {
this.properties = properties;
this.taskScheduler = taskScheduler;
}
private final NacosDiscoveryProperties properties;
private final TaskScheduler taskScheduler;
/**
* The constructor with {@link NacosDiscoveryProperties} bean and the optional.
* {@link TaskScheduler} bean
* @param properties {@link NacosDiscoveryProperties} bean
* @param taskScheduler the optional {@link TaskScheduler} bean
* @since 2.2.0
*/
public NacosWatch(NacosDiscoveryProperties properties,
public NacosWatch(NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties properties,
ObjectProvider<TaskScheduler> taskScheduler) {
this(properties, taskScheduler.getIfAvailable(NacosWatch::getTaskScheduler));
this.nacosServiceManager = nacosServiceManager;
this.properties = properties;
this.taskScheduler = taskScheduler.getIfAvailable(NacosWatch::getTaskScheduler);
}
private static ThreadPoolTaskScheduler getTaskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setBeanName("Nacso-Watch-Task-Scheduler");
taskScheduler.setBeanName("Nacos-Watch-Task-Scheduler");
taskScheduler.initialize();
return taskScheduler;
}
@ -98,19 +102,75 @@ public class NacosWatch implements ApplicationEventPublisherAware, SmartLifecycl
@Override
public void start() {
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::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
public void stop() {
if (this.running.compareAndSet(true, false) && this.watchFuture != null) {
// shutdown current user-thread,
// then the other daemon-threads will terminate automatic.
((ThreadPoolTaskScheduler) this.taskScheduler).shutdown();
this.watchFuture.cancel(true);
if (this.running.compareAndSet(true, false)) {
if (this.watchFuture != null) {
// shutdown current user-thread,
// then the other daemon-threads will terminate automatic.
((ThreadPoolTaskScheduler) this.taskScheduler).shutdown();
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 (NacosException e) {
log.error("namingService unsubscribe failed, properties:{}", properties,
e);
}
}
}

@ -22,6 +22,7 @@ import java.util.List;
import java.util.Map;
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.pojo.ServiceInfo;
import org.slf4j.Logger;
@ -35,15 +36,19 @@ 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
.getLogger(NacosDiscoveryEndpoint.class);
private NacosServiceManager nacosServiceManager;
private NacosDiscoveryProperties nacosDiscoveryProperties;
public NacosDiscoveryEndpoint(NacosDiscoveryProperties nacosDiscoveryProperties) {
public NacosDiscoveryEndpoint(NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties) {
this.nacosServiceManager = nacosServiceManager;
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
}
@ -55,7 +60,8 @@ public class NacosDiscoveryEndpoint {
Map<String, Object> result = new HashMap<>();
result.put("NacosDiscoveryProperties", nacosDiscoveryProperties);
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
NamingService namingService = nacosServiceManager
.getNamingService(nacosDiscoveryProperties.getNacosProperties());
List<ServiceInfo> subscribe = Collections.emptyList();
try {

@ -16,11 +16,14 @@
package com.alibaba.cloud.nacos.endpoint;
import java.util.Properties;
import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.discovery.actuate.health.NacosDiscoveryHealthIndicator;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.health.HealthIndicator;
@ -42,18 +45,21 @@ public class NacosDiscoveryEndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
@ConditionalOnAvailableEndpoint
public NacosDiscoveryEndpoint nacosDiscoveryEndpoint(
NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosDiscoveryEndpoint(nacosDiscoveryProperties);
return new NacosDiscoveryEndpoint(nacosServiceManager, nacosDiscoveryProperties);
}
@Bean
@ConditionalOnEnabledHealthIndicator("nacos-discovery")
public HealthIndicator nacosDiscoveryHealthIndicator(
NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties) {
Properties nacosProperties = nacosDiscoveryProperties.getNacosProperties();
return new NacosDiscoveryHealthIndicator(
nacosDiscoveryProperties.namingServiceInstance());
nacosServiceManager.getNamingService(nacosProperties));
}
}

@ -0,0 +1,38 @@
/*
* 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.event;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import org.springframework.context.ApplicationEvent;
/**
* @author yuhuangbin
*/
public class NacosDiscoveryInfoChangedEvent extends ApplicationEvent {
public NacosDiscoveryInfoChangedEvent(
NacosDiscoveryProperties nacosDiscoveryProperties) {
super(nacosDiscoveryProperties);
}
@Override
public NacosDiscoveryProperties getSource() {
return (NacosDiscoveryProperties) super.getSource();
}
}

@ -16,6 +16,7 @@
package com.alibaba.cloud.nacos.registry;
import com.alibaba.cloud.nacos.event.NacosDiscoveryInfoChangedEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -23,6 +24,7 @@ import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegis
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.event.EventListener;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@ -102,4 +104,14 @@ public class NacosAutoServiceRegistration
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 @@
package com.alibaba.cloud.nacos.registry;
import java.net.URI;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.PreservedMetadataKeys;
import org.springframework.cloud.client.DefaultServiceInstance;
@ -58,12 +58,16 @@ public class NacosRegistration implements Registration, ServiceInstance {
*/
public static final String MANAGEMENT_ENDPOINT_BASE_PATH = "management.endpoints.web.base-path";
private List<NacosRegistrationCustomizer> registrationCustomizers;
private NacosDiscoveryProperties nacosDiscoveryProperties;
private ApplicationContext context;
public NacosRegistration(NacosDiscoveryProperties nacosDiscoveryProperties,
public NacosRegistration(List<NacosRegistrationCustomizer> registrationCustomizers,
NacosDiscoveryProperties nacosDiscoveryProperties,
ApplicationContext context) {
this.registrationCustomizers = registrationCustomizers;
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
this.context = context;
}
@ -105,6 +109,17 @@ public class NacosRegistration implements Registration, ServiceInstance {
metadata.put(PreservedMetadataKeys.IP_DELETE_TIMEOUT,
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
@ -157,10 +172,6 @@ public class NacosRegistration implements Registration, ServiceInstance {
return nacosDiscoveryProperties;
}
public NamingService getNacosNamingService() {
return nacosDiscoveryProperties.namingServiceInstance();
}
@Override
public String toString() {
return "NacosRegistration{" + "nacosDiscoveryProperties="

@ -0,0 +1,30 @@
/*
* 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,13 +17,17 @@
package com.alibaba.cloud.nacos.registry;
import java.util.List;
import java.util.Properties;
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.pojo.Instance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.util.StringUtils;
@ -41,6 +45,9 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> {
private final NacosDiscoveryProperties nacosDiscoveryProperties;
@Autowired
private NacosServiceManager nacosServiceManager;
public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
}
@ -101,7 +108,12 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> {
@Override
public void close() {
try {
nacosServiceManager.nacosServiceShutDown();
}
catch (NacosException e) {
log.error("Nacos namingService shutDown failed", e);
}
}
@Override
@ -124,7 +136,8 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> {
}
try {
nacosDiscoveryProperties.namingMaintainServiceInstance()
Properties nacosProperties = nacosDiscoveryProperties.getNacosProperties();
nacosServiceManager.getNamingMaintainService(nacosProperties)
.updateInstance(serviceId, instance);
}
catch (Exception e) {
@ -138,8 +151,7 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> {
String serviceName = registration.getServiceId();
try {
List<Instance> instances = nacosDiscoveryProperties.namingServiceInstance()
.getAllInstances(serviceName);
List<Instance> instances = namingService().getAllInstances(serviceName);
for (Instance instance : instances) {
if (instance.getIp().equalsIgnoreCase(nacosDiscoveryProperties.getIp())
&& instance.getPort() == nacosDiscoveryProperties.getPort()) {
@ -161,12 +173,13 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> {
instance.setClusterName(nacosDiscoveryProperties.getClusterName());
instance.setEnabled(nacosDiscoveryProperties.isInstanceEnabled());
instance.setMetadata(registration.getMetadata());
instance.setEphemeral(nacosDiscoveryProperties.isEphemeral());
return instance;
}
private NamingService namingService() {
return nacosDiscoveryProperties.namingServiceInstance();
return nacosServiceManager
.getNamingService(nacosDiscoveryProperties.getNacosProperties());
}
}

@ -16,10 +16,13 @@
package com.alibaba.cloud.nacos.registry;
import java.util.List;
import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -54,9 +57,11 @@ public class NacosServiceRegistryAutoConfiguration {
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosRegistration nacosRegistration(
ObjectProvider<List<NacosRegistrationCustomizer>> registrationCustomizers,
NacosDiscoveryProperties nacosDiscoveryProperties,
ApplicationContext context) {
return new NacosRegistration(nacosDiscoveryProperties, context);
return new NacosRegistration(registrationCustomizers.getIfAvailable(),
nacosDiscoveryProperties, context);
}
@Bean

@ -21,6 +21,7 @@ import java.util.Objects;
import java.util.stream.Collectors;
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.pojo.Instance;
import com.netflix.client.config.IClientConfig;
@ -47,16 +48,20 @@ public class NacosRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Autowired
private NacosServiceManager nacosServiceManager;
@Override
public Server choose(Object key) {
try {
String clusterName = this.nacosDiscoveryProperties.getClusterName();
String group = this.nacosDiscoveryProperties.getGroup();
DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
String name = loadBalancer.getName();
NamingService namingService = nacosDiscoveryProperties
.namingServiceInstance();
List<Instance> instances = namingService.selectInstances(name, true);
NamingService namingService = nacosServiceManager
.getNamingService(nacosDiscoveryProperties.getNacosProperties());
List<Instance> instances = namingService.selectInstances(name, group, true);
if (CollectionUtils.isEmpty(instances)) {
LOGGER.warn("no instance in service {}", name);
return null;

@ -29,6 +29,12 @@
"defaultValue": true,
"description": "If instance is enabled to accept request. The default value is true."
},
{
"name": "spring.cloud.nacos.discovery.ephemeral",
"type": "java.lang.Boolean",
"defaultValue": true,
"description": "If instance is ephemeral.The default value is true."
},
{
"name": "spring.cloud.nacos.discovery.namingLoadCacheAtStart",
"type": "java.lang.Boolean",

@ -5,6 +5,7 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration,\
com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration,\
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=\
com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration

@ -22,6 +22,7 @@ import java.util.LinkedList;
import java.util.List;
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.pojo.Instance;
@ -60,16 +61,19 @@ public class NacosServiceDiscoveryTest {
NacosDiscoveryProperties nacosDiscoveryProperties = mock(
NacosDiscoveryProperties.class);
NacosServiceManager nacosServiceManager = mock(NacosServiceManager.class);
NamingService namingService = mock(NamingService.class);
when(nacosDiscoveryProperties.namingServiceInstance()).thenReturn(namingService);
when(nacosServiceManager
.getNamingService(nacosDiscoveryProperties.getNacosProperties()))
.thenReturn(namingService);
when(nacosDiscoveryProperties.getGroup()).thenReturn("DEFAULT");
when(namingService.selectInstances(eq(serviceName), eq("DEFAULT"), eq(true)))
.thenReturn(instances);
NacosServiceDiscovery serviceDiscovery = new NacosServiceDiscovery(
nacosDiscoveryProperties);
nacosDiscoveryProperties, nacosServiceManager);
List<ServiceInstance> serviceInstances = serviceDiscovery
.getInstances(serviceName);
@ -99,16 +103,19 @@ public class NacosServiceDiscoveryTest {
NacosDiscoveryProperties nacosDiscoveryProperties = mock(
NacosDiscoveryProperties.class);
NacosServiceManager nacosServiceManager = mock(NacosServiceManager.class);
NamingService namingService = mock(NamingService.class);
when(nacosDiscoveryProperties.namingServiceInstance()).thenReturn(namingService);
when(nacosServiceManager
.getNamingService(nacosDiscoveryProperties.getNacosProperties()))
.thenReturn(namingService);
when(nacosDiscoveryProperties.getGroup()).thenReturn("DEFAULT");
when(namingService.getServicesOfServer(eq(1), eq(Integer.MAX_VALUE),
eq("DEFAULT"))).thenReturn(nacosServices);
NacosServiceDiscovery serviceDiscovery = new NacosServiceDiscovery(
nacosDiscoveryProperties);
nacosDiscoveryProperties, nacosServiceManager);
List<String> services = serviceDiscovery.getServices();

@ -328,4 +328,9 @@ public class MockNamingService implements NamingService {
return null;
}
@Override
public void shutDown() throws NacosException {
}
}

@ -22,6 +22,7 @@ import java.util.Map;
import java.util.Properties;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration;
import com.alibaba.cloud.nacos.endpoint.NacosDiscoveryEndpoint;
import com.alibaba.nacos.api.NacosFactory;
@ -86,6 +87,9 @@ public class NacosAutoServiceRegistrationTests {
@Autowired
private NacosDiscoveryProperties properties;
@Autowired
private NacosServiceManager nacosServiceManager;
@Autowired
private InetUtils inetUtils;
@ -207,7 +211,7 @@ public class NacosAutoServiceRegistrationTests {
private void checkoutEndpoint() throws Exception {
NacosDiscoveryEndpoint nacosDiscoveryEndpoint = new NacosDiscoveryEndpoint(
properties);
nacosServiceManager, properties);
Map<String, Object> map = nacosDiscoveryEndpoint.nacosDiscovery();
assertThat(properties).isEqualTo(map.get("NacosDiscoveryProperties"));

@ -0,0 +1,104 @@
/*
* 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");
};
}
}
}

@ -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();
}
}

@ -19,11 +19,11 @@ package com.alibaba.cloud.seata.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import io.seata.common.util.StringUtils;
import io.seata.core.context.RootContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
/**
@ -43,41 +43,42 @@ public class SeataHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
String xid = RootContext.getXID();
String rpcXid = request.getHeader(RootContext.KEY_XID);
if (log.isDebugEnabled()) {
log.debug("xid in RootContext {} xid in RpcContext {}", xid, rpcXid);
}
if (xid == null && rpcXid != null) {
if (StringUtils.isBlank(xid) && rpcXid != null) {
RootContext.bind(rpcXid);
if (log.isDebugEnabled()) {
log.debug("bind {} to RootContext", rpcXid);
}
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception e) {
if (StringUtils.isNotBlank(RootContext.getXID())) {
String rpcXid = request.getHeader(RootContext.KEY_XID);
String rpcXid = request.getHeader(RootContext.KEY_XID);
if (StringUtils.isEmpty(rpcXid)) {
return;
}
if (StringUtils.isEmpty(rpcXid)) {
return;
}
String unbindXid = RootContext.unbind();
if (log.isDebugEnabled()) {
log.debug("unbind {} from RootContext", unbindXid);
}
if (!rpcXid.equalsIgnoreCase(unbindXid)) {
log.warn("xid in change during RPC from {} to {}", rpcXid, unbindXid);
if (unbindXid != null) {
RootContext.bind(unbindXid);
log.warn("bind {} back to RootContext", unbindXid);
String unbindXid = RootContext.unbind();
if (log.isDebugEnabled()) {
log.debug("unbind {} from RootContext", unbindXid);
}
if (!rpcXid.equalsIgnoreCase(unbindXid)) {
log.warn("xid in change during RPC from {} to {}", rpcXid, unbindXid);
if (unbindXid != null) {
RootContext.bind(unbindXid);
log.warn("bind {} back to RootContext", unbindXid);
}
}
}
}

@ -153,6 +153,7 @@
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<optional>true</optional>
</dependency>
<dependency>

@ -31,7 +31,6 @@ import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
import com.alibaba.csp.sentinel.util.AppNameUtil;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
@ -79,9 +78,9 @@ public class SentinelAutoConfiguration {
System.setProperty(LogBase.LOG_NAME_USE_PID,
String.valueOf(properties.getLog().isSwitchPid()));
}
if (StringUtils.isEmpty(System.getProperty(AppNameUtil.APP_NAME))
if (StringUtils.isEmpty(System.getProperty(SentinelConfig.APP_NAME_PROP_KEY))
&& StringUtils.hasText(projectName)) {
System.setProperty(AppNameUtil.APP_NAME, projectName);
System.setProperty(SentinelConfig.APP_NAME_PROP_KEY, projectName);
}
if (StringUtils.isEmpty(System.getProperty(TransportConfig.SERVER_PORT))
&& StringUtils.hasText(properties.getTransport().getPort())) {

@ -19,7 +19,7 @@ package com.alibaba.cloud.sentinel.endpoint;
import com.alibaba.cloud.sentinel.SentinelProperties;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@ -36,7 +36,7 @@ public class SentinelEndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
@ConditionalOnAvailableEndpoint
public SentinelEndpoint sentinelEndPoint(SentinelProperties sentinelProperties) {
return new SentinelEndpoint(sentinelProperties);
}

@ -105,7 +105,7 @@ public class SentinelHealthIndicator extends AbstractHealthIndicator {
// If failed to send heartbeat message, means that the Dashboard is DOWN
dashboardUp = false;
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",
consoleServerList)));
}
@ -138,7 +138,7 @@ public class SentinelHealthIndicator extends AbstractHealthIndicator {
// DOWN
dataSourceUp = false;
dataSourceDetailMap.put(dataSourceBeanName,
new Status(Status.DOWN.getCode(), e.getMessage()));
new Status(Status.UNKNOWN.getCode(), e.getMessage()));
}
}
@ -147,7 +147,7 @@ public class SentinelHealthIndicator extends AbstractHealthIndicator {
builder.up().withDetails(detailMap);
}
else {
builder.down().withDetails(detailMap);
builder.unknown().withDetails(detailMap);
}
}

@ -28,8 +28,6 @@ import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.log.LogBase;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
@ -116,13 +114,6 @@ public class SentinelAutoConfigurationTests {
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
rule.setStrategy(RuleConstant.STRATEGY_DIRECT);
FlowRuleManager.loadRules(Arrays.asList(rule));
DegradeRule degradeRule = new DegradeRule();
degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
degradeRule.setResource("GET:" + degradeUrl);
degradeRule.setCount(0);
degradeRule.setTimeWindow(60);
DegradeRuleManager.loadRules(Arrays.asList(degradeRule));
}
@Test
@ -208,7 +199,7 @@ public class SentinelAutoConfigurationTests {
}
@Test
public void testFlowRestTemplate() {
public void testRestTemplateBlockHandler() {
assertThat(restTemplate.getInterceptors().size()).isEqualTo(2);
assertThat(restTemplateWithBlockClass.getInterceptors().size()).isEqualTo(1);
@ -236,15 +227,6 @@ public class SentinelAutoConfigurationTests {
}).isInstanceOf(RestClientException.class);
}
@Test
public void testFallbackRestTemplate() {
ResponseEntity responseEntity = restTemplateWithFallbackClass
.getForEntity(degradeUrl, String.class);
assertThat(responseEntity.getBody()).isEqualTo("Oops fallback");
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@Configuration
static class SentinelTestConfiguration {

@ -109,9 +109,9 @@ public class SentinelHealthIndicatorTests {
Health health = sentinelHealthIndicator.health();
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
assertThat(health.getDetails().get("dashboard")).isEqualTo(
new Status(Status.DOWN.getCode(), "localhost:8080 can't be connected"));
assertThat(health.getStatus()).isEqualTo(Status.UNKNOWN);
assertThat(health.getDetails().get("dashboard")).isEqualTo(new Status(
Status.UNKNOWN.getCode(), "localhost:8080 can't be connected"));
}
@Test
@ -163,13 +163,13 @@ public class SentinelHealthIndicatorTests {
Health health = sentinelHealthIndicator.health();
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
assertThat(health.getStatus()).isEqualTo(Status.UNKNOWN);
Map<String, Status> dataSourceDetailMap = (Map<String, Status>) health
.getDetails().get("dataSource");
assertThat(dataSourceDetailMap.get("ds1-sentinel-file-datasource"))
.isEqualTo(Status.UP);
assertThat(dataSourceDetailMap.get("ds2-sentinel-file-datasource"))
.isEqualTo(new Status(Status.DOWN.getCode(), "fileDataSource2 error"));
.isEqualTo(new Status(Status.UNKNOWN.getCode(), "fileDataSource2 error"));
}
}

@ -16,6 +16,9 @@
package com.alibaba.cloud.sidecar;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
@ -28,11 +31,14 @@ import org.springframework.core.env.ConfigurableEnvironment;
/**
* @author www.itmuch.com
* @author yuhuangbin
*/
public class SidecarHealthChecker {
private static final Logger log = LoggerFactory.getLogger(SidecarHealthChecker.class);
private final Map<String, SidecarInstanceCache> sidecarInstanceCacheMap = new ConcurrentHashMap<>();
private final SidecarDiscoveryClient sidecarDiscoveryClient;
private final HealthIndicator healthIndicator;
@ -52,26 +58,60 @@ public class SidecarHealthChecker {
public void check() {
Schedulers.single().schedulePeriodically(() -> {
String applicationName = environment.getProperty("spring.application.name");
String ip = sidecarProperties.getIp();
Integer port = sidecarProperties.getPort();
Status status = healthIndicator.health().getStatus();
String applicationName = environment.getProperty("spring.application.name");
instanceCache(applicationName, ip, port, status);
if (status.equals(Status.UP)) {
this.sidecarDiscoveryClient.registerInstance(applicationName, ip, port);
log.debug(
"Health check success. register this instance. applicationName = {}, ip = {}, port = {}, status = {}",
applicationName, ip, port, status);
if (needRegister(applicationName, ip, port, status)) {
this.sidecarDiscoveryClient.registerInstance(applicationName, ip,
port);
log.info(
"Polyglot service changed and Health check success. register the new instance. applicationName = {}, ip = {}, port = {}, status = {}",
applicationName, ip, port, status);
}
}
else {
log.warn(
"Health check failed. unregister this instance. applicationName = {}, ip = {}, port = {}, status = {}",
applicationName, ip, port, status);
this.sidecarDiscoveryClient.deregisterInstance(applicationName, ip, port);
sidecarInstanceCacheMap.put(applicationName,
buildCache(ip, port, status));
}
}, 0, sidecarProperties.getHealthCheckInterval(), TimeUnit.MILLISECONDS);
}
private void instanceCache(String applicationName, String ip, Integer port,
Status status) {
sidecarInstanceCacheMap.putIfAbsent(applicationName,
buildCache(ip, port, status));
}
private boolean needRegister(String applicationName, String ip, Integer port,
Status status) {
SidecarInstanceCache cacheRecord = sidecarInstanceCacheMap.get(applicationName);
SidecarInstanceCache cache = buildCache(ip, port, status);
if (!Objects.equals(cache, cacheRecord)) {
// modify the cache info
sidecarInstanceCacheMap.put(applicationName, cache);
return true;
}
return false;
}
private SidecarInstanceCache buildCache(String ip, Integer port, Status status) {
SidecarInstanceCache cache = new SidecarInstanceCache();
cache.setIp(ip);
cache.setPort(port);
cache.setStatus(status);
return cache;
}
}

@ -0,0 +1,76 @@
/*
* 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.sidecar;
import java.util.Objects;
import org.springframework.boot.actuate.health.Status;
/**
* @author yuhuangbin
*/
public class SidecarInstanceCache {
private String ip;
private Integer port;
private Status status;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SidecarInstanceCache that = (SidecarInstanceCache) o;
return Objects.equals(ip, that.ip) && Objects.equals(port, that.port)
&& Objects.equals(status, that.status);
}
@Override
public int hashCode() {
return Objects.hash(ip, port, status);
}
}

@ -17,6 +17,7 @@
package com.alibaba.cloud.sidecar.nacos;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration;
import com.alibaba.cloud.sidecar.SidecarAutoConfiguration;
import com.alibaba.cloud.sidecar.SidecarDiscoveryClient;
@ -49,8 +50,10 @@ public class SidecarNacosAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SidecarDiscoveryClient sidecarDiscoveryClient(
NacosServiceManager nacosServiceManager,
SidecarNacosDiscoveryProperties sidecarNacosDiscoveryProperties) {
return new SidecarNacosDiscoveryClient(sidecarNacosDiscoveryProperties);
return new SidecarNacosDiscoveryClient(nacosServiceManager,
sidecarNacosDiscoveryProperties);
}
}

@ -16,8 +16,10 @@
package com.alibaba.cloud.sidecar.nacos;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.sidecar.SidecarDiscoveryClient;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -29,19 +31,21 @@ public class SidecarNacosDiscoveryClient implements SidecarDiscoveryClient {
private static final Logger log = LoggerFactory
.getLogger(SidecarNacosDiscoveryClient.class);
private NacosServiceManager nacosServiceManager;
private final SidecarNacosDiscoveryProperties sidecarNacosDiscoveryProperties;
public SidecarNacosDiscoveryClient(
public SidecarNacosDiscoveryClient(NacosServiceManager nacosServiceManager,
SidecarNacosDiscoveryProperties sidecarNacosDiscoveryProperties) {
this.nacosServiceManager = nacosServiceManager;
this.sidecarNacosDiscoveryProperties = sidecarNacosDiscoveryProperties;
}
@Override
public void registerInstance(String applicationName, String ip, Integer port) {
try {
this.sidecarNacosDiscoveryProperties.namingServiceInstance().registerInstance(
applicationName, sidecarNacosDiscoveryProperties.getGroup(), ip,
port);
this.namingService().registerInstance(applicationName,
sidecarNacosDiscoveryProperties.getGroup(), ip, port);
}
catch (NacosException e) {
log.warn("nacos exception happens", e);
@ -51,13 +55,17 @@ public class SidecarNacosDiscoveryClient implements SidecarDiscoveryClient {
@Override
public void deregisterInstance(String applicationName, String ip, Integer port) {
try {
this.sidecarNacosDiscoveryProperties.namingServiceInstance()
.deregisterInstance(applicationName,
sidecarNacosDiscoveryProperties.getGroup(), ip, port);
this.namingService().deregisterInstance(applicationName,
sidecarNacosDiscoveryProperties.getGroup(), ip, port);
}
catch (NacosException e) {
log.warn("nacos exception happens", e);
}
}
private NamingService namingService() {
return nacosServiceManager
.getNamingService(sidecarNacosDiscoveryProperties.getNacosProperties());
}
}

@ -16,8 +16,6 @@
package com.alibaba.cloud.sidecar.nacos;
import java.net.SocketException;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.sidecar.SidecarProperties;
@ -35,7 +33,7 @@ public class SidecarNacosDiscoveryProperties extends NacosDiscoveryProperties {
}
@Override
public void init() throws SocketException {
public void init() throws Exception {
super.init();
String ip = sidecarProperties.getIp();

@ -1,6 +1,6 @@
<?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"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>

@ -18,7 +18,7 @@ package com.alibaba.cloud.dubbo.actuate;
import com.alibaba.cloud.dubbo.actuate.endpoint.DubboRestMetadataEndpoint;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -39,7 +39,7 @@ public class DubboMetadataEndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
@ConditionalOnAvailableEndpoint
public DubboRestMetadataEndpoint dubboRestMetadataEndpoint() {
return new DubboRestMetadataEndpoint();
}

@ -17,18 +17,18 @@
package com.alibaba.cloud.dubbo.autoconfigure;
import java.util.Collection;
import java.util.Optional;
import java.util.function.Supplier;
import com.alibaba.cloud.dubbo.metadata.DubboProtocolConfigSupplier;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.metadata.repository.MetadataServiceInstanceSelector;
import com.alibaba.cloud.dubbo.metadata.repository.RandomServiceInstanceSelector;
import com.alibaba.cloud.dubbo.metadata.repository.ServiceInstanceSelector;
import com.alibaba.cloud.dubbo.metadata.resolver.DubboServiceBeanMetadataResolver;
import com.alibaba.cloud.dubbo.metadata.resolver.MetadataResolver;
import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceExporter;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy;
import com.alibaba.cloud.dubbo.service.IntrospectiveDubboMetadataService;
import com.alibaba.cloud.dubbo.util.DubboMetadataUtils;
import com.alibaba.cloud.dubbo.util.JSONUtils;
import feign.Contract;
import org.apache.dubbo.config.ProtocolConfig;
@ -44,7 +44,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.util.CollectionUtils;
/**
* Spring Boot Auto-Configuration class for Dubbo Metadata.
@ -53,7 +52,8 @@ import org.springframework.util.CollectionUtils;
*/
@Configuration(proxyBeanMethods = false)
@Import({ DubboServiceMetadataRepository.class, IntrospectiveDubboMetadataService.class,
DubboMetadataServiceExporter.class, JSONUtils.class })
DubboMetadataServiceExporter.class, JSONUtils.class,
DubboMetadataServiceProxy.class, DubboMetadataUtils.class })
public class DubboMetadataAutoConfiguration {
@Autowired
@ -73,9 +73,8 @@ public class DubboMetadataAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MetadataServiceInstanceSelector metadataServiceInstanceSelector() {
return serviceInstances -> CollectionUtils.isEmpty(serviceInstances)
? Optional.empty() : serviceInstances.stream().findAny();
public ServiceInstanceSelector metadataServiceInstanceSelector() {
return new RandomServiceInstanceSelector();
}
@Bean
@ -84,15 +83,7 @@ public class DubboMetadataAutoConfiguration {
return new DubboProtocolConfigSupplier(protocols);
}
@Bean
@ConditionalOnMissingBean
public DubboMetadataServiceProxy dubboMetadataConfigServiceProxy(
DubboGenericServiceFactory factory) {
return new DubboMetadataServiceProxy(factory);
}
// Event-Handling
@EventListener(ServiceBeanExportedEvent.class)
public void onServiceBeanExported(ServiceBeanExportedEvent event) {
ServiceBean serviceBean = event.getServiceBean();

@ -24,15 +24,11 @@ import com.alibaba.cloud.dubbo.service.parameter.RequestBodyServiceParameterReso
import com.alibaba.cloud.dubbo.service.parameter.RequestHeaderServiceParameterResolver;
import com.alibaba.cloud.dubbo.service.parameter.RequestParamServiceParameterResolver;
import org.springframework.beans.factory.annotation.Autowired;
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.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertyResolver;
/**
* Spring Boot Auto-Configuration class for Dubbo Service.
@ -49,17 +45,6 @@ public class DubboServiceAutoConfiguration {
return new DubboGenericServiceFactory();
}
/**
* Build a primary {@link PropertyResolver} bean to {@link Autowired @Autowired}.
* @param environment {@link Environment}
* @return alias bean for {@link Environment}
*/
@Bean
@Primary
public PropertyResolver primaryPropertyResolver(Environment environment) {
return environment;
}
@Configuration(proxyBeanMethods = false)
@Import({ DubboGenericServiceExecutionContextFactory.class,
RequestParamServiceParameterResolver.class,

@ -33,6 +33,7 @@ import com.alibaba.cloud.dubbo.registry.AbstractSpringCloudRegistry;
import com.alibaba.cloud.dubbo.registry.event.ServiceInstancesChangedEvent;
import com.alibaba.cloud.dubbo.registry.event.SubscribedServicesChangedEvent;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.discovery.NacosWatch;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
@ -56,6 +57,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@ -135,6 +137,9 @@ public class DubboServiceDiscoveryAutoConfiguration {
*/
private final ObjectProvider<Predicate<HeartbeatEvent>> heartbeatEventChangedPredicate;
@Value("${spring.application.name:${dubbo.application.name:application}}")
private String currentApplicationName;
public DubboServiceDiscoveryAutoConfiguration(
DubboServiceMetadataRepository dubboServiceMetadataRepository,
ApplicationEventPublisher applicationEventPublisher,
@ -154,10 +159,12 @@ public class DubboServiceDiscoveryAutoConfiguration {
* NotifyListener)
*/
private void dispatchServiceInstancesChangedEvent(String serviceName,
Collection<ServiceInstance> serviceInstances) {
if (!hasText(serviceName) || serviceInstances == null) {
List<ServiceInstance> serviceInstances) {
if (!hasText(serviceName) || Objects.equals(currentApplicationName, serviceName)
|| serviceInstances == null) {
return;
}
ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent(serviceName,
serviceInstances);
if (logger.isInfoEnabled()) {
@ -512,8 +519,10 @@ public class DubboServiceDiscoveryAutoConfiguration {
*/
private final Set<String> listeningServices;
NacosConfiguration(NacosDiscoveryProperties nacosDiscoveryProperties) {
this.namingService = nacosDiscoveryProperties.namingServiceInstance();
NacosConfiguration(NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties) {
this.namingService = nacosServiceManager
.getNamingService(nacosDiscoveryProperties.getNacosProperties());
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
this.listeningServices = new ConcurrentSkipListSet<>();
}

@ -22,6 +22,8 @@ import java.util.Set;
import org.springframework.boot.context.properties.ConfigurationProperties;
import static com.alibaba.cloud.dubbo.util.DubboCloudConstants.CONFIG_PROPERTY_PREFIX;
import static com.alibaba.cloud.dubbo.util.DubboCloudConstants.DUBBO_CLOUD_REGISTRY_PROPERTY_VALUE;
import static org.springframework.util.StringUtils.commaDelimitedListToStringArray;
import static org.springframework.util.StringUtils.hasText;
import static org.springframework.util.StringUtils.trimAllWhitespace;
@ -31,7 +33,7 @@ import static org.springframework.util.StringUtils.trimAllWhitespace;
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@ConfigurationProperties(prefix = "dubbo.cloud")
@ConfigurationProperties(prefix = CONFIG_PROPERTY_PREFIX)
public class DubboCloudProperties {
/**
@ -47,6 +49,8 @@ public class DubboCloudProperties {
*/
private String subscribedServices = ALL_DUBBO_SERVICES;
private String registryType = DUBBO_CLOUD_REGISTRY_PROPERTY_VALUE;
public String getSubscribedServices() {
return subscribedServices;
}
@ -79,4 +83,12 @@ public class DubboCloudProperties {
return Collections.unmodifiableSet(subscribedServices);
}
public String getRegistryType() {
return registryType;
}
public void setRegistryType(String registryType) {
this.registryType = registryType;
}
}

@ -22,9 +22,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.PostConstruct;
@ -38,6 +36,7 @@ import com.alibaba.cloud.dubbo.registry.event.SubscribedServicesChangedEvent;
import com.alibaba.cloud.dubbo.service.DubboMetadataService;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceExporter;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy;
import com.alibaba.cloud.dubbo.util.DubboMetadataUtils;
import com.alibaba.cloud.dubbo.util.JSONUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
@ -61,15 +60,15 @@ import org.springframework.util.MultiValueMap;
import static com.alibaba.cloud.dubbo.env.DubboCloudProperties.ALL_DUBBO_SERVICES;
import static com.alibaba.cloud.dubbo.http.DefaultHttpRequest.builder;
import static java.lang.String.format;
import static java.lang.String.valueOf;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
import static java.util.Collections.unmodifiableSet;
import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_PREFIX;
import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URLS_PROPERTY_NAME;
import static org.springframework.util.CollectionUtils.isEmpty;
import static org.springframework.util.StringUtils.hasText;
@ -85,14 +84,15 @@ public class DubboServiceMetadataRepository
/**
* The prefix of {@link DubboMetadataService} : "dubbo.metadata-service.".
*/
public static final String DUBBO_METADATA_SERVICE_PREFIX = "dubbo.metadata-service.";
@Deprecated
public static final String DUBBO_METADATA_SERVICE_PREFIX = METADATA_SERVICE_PREFIX;
/**
* The {@link URL URLs} property name of {@link DubboMetadataService} :
* "dubbo.metadata-service.urls".
*/
public static final String DUBBO_METADATA_SERVICE_URLS_PROPERTY_NAME = DUBBO_METADATA_SERVICE_PREFIX
+ "urls";
@Deprecated
public static final String DUBBO_METADATA_SERVICE_URLS_PROPERTY_NAME = METADATA_SERVICE_URLS_PROPERTY_NAME;
/**
* The {@link String#format(String, Object...) pattern} of dubbo protocols port.
@ -108,11 +108,6 @@ public class DubboServiceMetadataRepository
*/
private final Object monitor = new Object();
/**
* A {@link Set} of service names that had been initialized.
*/
private final Set<String> initializedServices = new LinkedHashSet<>();
/**
* All exported {@link URL urls} {@link Map} whose key is the return value of
* {@link URL#getServiceKey()} method and value is the {@link List} of {@link URL
@ -122,12 +117,6 @@ public class DubboServiceMetadataRepository
// =================================== Registration
// =================================== //
/**
* The subscribed {@link URL urls} {@link Map} of {@link DubboMetadataService}, whose
* key is the return value of {@link URL#getServiceKey()} method and value is the
* {@link List} of {@link URL URLs}.
*/
private final MultiValueMap<String, URL> subscribedDubboMetadataServiceURLs = new LinkedMultiValueMap<>();
// ====================================================================================
// //
@ -172,7 +161,7 @@ public class DubboServiceMetadataRepository
private DiscoveryClient discoveryClient;
@Autowired
private MetadataServiceInstanceSelector metadataServiceInstanceSelector;
private ServiceInstanceSelector serviceInstanceSelector;
@Autowired
private JSONUtils jsonUtils;
@ -180,6 +169,9 @@ public class DubboServiceMetadataRepository
@Autowired
private InetUtils inetUtils;
@Autowired
private DubboMetadataUtils dubboMetadataUtils;
@Value("${spring.application.name}")
private String currentApplicationName;
@ -275,28 +267,11 @@ public class DubboServiceMetadataRepository
* @param serviceName service of name
*/
public void initializeMetadata(String serviceName) {
synchronized (monitor) {
if (initializedServices.contains(serviceName)) {
if (logger.isDebugEnabled()) {
logger.debug(
"The metadata of Dubbo service[name : {}] has been initialized",
serviceName);
}
}
else {
if (logger.isInfoEnabled()) {
logger.info(
"The metadata of Dubbo service[name : {}] is about to be initialized",
serviceName);
}
if (initSubscribedDubboMetadataService(serviceName)) {
// mark this service name having been initialized
initializedServices.add(serviceName);
}
initDubboRestServiceMetadataRepository(serviceName);
}
}
}
private DubboMetadataService getProxy(String serviceName) {
return dubboMetadataConfigServiceProxy.getProxy(serviceName);
}
/**
@ -307,15 +282,8 @@ public class DubboServiceMetadataRepository
*/
public void removeMetadataAndInitializedService(String serviceName, URL url) {
synchronized (monitor) {
initializedServices.remove(serviceName);
dubboMetadataConfigServiceProxy.removeProxy(serviceName);
dubboRestServiceMetadataRepository.remove(serviceName);
// fix #1260 if the subscribedDubboMetadataServiceURLs removed failold meta
// information will be retained
if (DubboMetadataService.class.getName().equals(url.getServiceInterface())) {
String serviceKey = url.getServiceKey();
subscribedDubboMetadataServiceURLs.remove(serviceKey);
}
}
}
@ -345,8 +313,7 @@ public class DubboServiceMetadataRepository
private void addDubboMetadataServiceURLsMetadata(Map<String, String> metadata,
List<URL> dubboMetadataServiceURLs) {
String dubboMetadataServiceURLsJSON = jsonUtils.toJSON(dubboMetadataServiceURLs);
metadata.put(DUBBO_METADATA_SERVICE_URLS_PROPERTY_NAME,
dubboMetadataServiceURLsJSON);
metadata.put(METADATA_SERVICE_URLS_PROPERTY_NAME, dubboMetadataServiceURLsJSON);
}
private void addDubboProtocolsPortMetadata(Map<String, String> metadata) {
@ -359,15 +326,6 @@ public class DubboServiceMetadataRepository
});
}
/**
* Get the property name of Dubbo Protocol.
* @param protocol Dubbo Protocol
* @return non-null
*/
public String getDubboProtocolPropertyName(String protocol) {
return format(DUBBO_PROTOCOLS_PORT_PROPERTY_NAME_PATTERN, protocol);
}
/**
* Publish the {@link Set} of {@link ServiceRestMetadata}.
* @param serviceRestMetadataSet the {@link Set} of {@link ServiceRestMetadata}
@ -389,25 +347,6 @@ public class DubboServiceMetadataRepository
return unmodifiableSet(serviceRestMetadata);
}
public List<URL> findSubscribedDubboMetadataServiceURLs(String serviceName,
String group, String version, String protocol) {
String serviceKey = URL.buildKey(serviceName, group, version);
List<URL> urls = null;
synchronized (monitor) {
urls = subscribedDubboMetadataServiceURLs.get(serviceKey);
}
if (isEmpty(urls)) {
return emptyList();
}
return hasText(protocol) ? urls.stream()
.filter(url -> url.getProtocol().equalsIgnoreCase(protocol))
.collect(Collectors.toList()) : unmodifiableList(urls);
}
/**
* The specified service is subscribe or not.
* @param serviceName the service name
@ -457,24 +396,13 @@ public class DubboServiceMetadataRepository
return allExportedURLs.keySet();
}
/**
* Get the {@link URL urls} that {@link DubboMetadataService} exported by the
* specified {@link ServiceInstance}.
* @param serviceInstance {@link ServiceInstance}
* @return the mutable {@link URL urls}
*/
public List<URL> getDubboMetadataServiceURLs(ServiceInstance serviceInstance) {
Map<String, String> metadata = serviceInstance.getMetadata();
String dubboURLsJSON = metadata.get(DUBBO_METADATA_SERVICE_URLS_PROPERTY_NAME);
return jsonUtils.toURLs(dubboURLsJSON);
}
public Integer getDubboProtocolPort(ServiceInstance serviceInstance,
String protocol) {
String protocolProperty = getDubboProtocolPropertyName(protocol);
Map<String, String> metadata = serviceInstance.getMetadata();
String protocolPort = metadata.get(protocolProperty);
return hasText(protocolPort) ? Integer.valueOf(protocolPort) : null;
return dubboMetadataUtils.getDubboProtocolPort(serviceInstance, protocol);
}
private String getDubboProtocolPropertyName(String protocol) {
return dubboMetadataUtils.getDubboProtocolPropertyName(protocol);
}
public List<URL> getExportedURLs(String serviceInterface, String group,
@ -490,6 +418,11 @@ public class DubboServiceMetadataRepository
protected void initDubboRestServiceMetadataRepository(String serviceName) {
if (dubboRestServiceMetadataRepository.containsKey(serviceName)) {
if (logger.isDebugEnabled()) {
logger.debug(
"The metadata of Dubbo service[name : {}] has been initialized",
serviceName);
}
return;
}
@ -598,8 +531,7 @@ public class DubboServiceMetadataRepository
Set<ServiceRestMetadata> metadata = emptySet();
DubboMetadataService dubboMetadataService = dubboMetadataConfigServiceProxy
.getProxy(serviceName);
DubboMetadataService dubboMetadataService = getProxy(serviceName);
if (dubboMetadataService != null) {
try {
@ -624,68 +556,29 @@ public class DubboServiceMetadataRepository
subscribedServices.remove(currentApplicationName);
}
protected Boolean initSubscribedDubboMetadataService(String serviceName) {
// this need to judge whether the initialization is successful or not. The failed
// initialization will not change the initializedServices
Optional<ServiceInstance> optionalServiceInstance = metadataServiceInstanceSelector
.choose(discoveryClient.getInstances(serviceName));
if (!((Optional) optionalServiceInstance).isPresent()) {
return false;
}
ServiceInstance serviceInstance = optionalServiceInstance.get();
if (null == serviceInstance) {
return false;
}
List<URL> dubboMetadataServiceURLs = getDubboMetadataServiceURLs(serviceInstance);
if (dubboMetadataServiceURLs.isEmpty()) {
return false;
}
for (URL dubboMetadataServiceURL : dubboMetadataServiceURLs) {
try {
initSubscribedDubboMetadataServiceURL(dubboMetadataServiceURL);
DubboMetadataService dubboMetadataService = dubboMetadataConfigServiceProxy
.getProxy(serviceName);
if (dubboMetadataService == null) {
dubboMetadataService = initDubboMetadataServiceProxy(
dubboMetadataServiceURL);
}
if (dubboMetadataService == null) {
removeMetadataAndInitializedService(serviceName,
dubboMetadataServiceURL);
return false;
}
}
catch (Throwable e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage(), e);
}
return false;
}
}
initDubboRestServiceMetadataRepository(serviceName);
return true;
}
private void initSubscribedDubboMetadataServiceURL(URL dubboMetadataServiceURL) {
// add subscriptions
String serviceKey = dubboMetadataServiceURL.getServiceKey();
subscribedDubboMetadataServiceURLs.add(serviceKey, dubboMetadataServiceURL);
}
private DubboMetadataService initDubboMetadataServiceProxy(
URL dubboMetadataServiceURL) {
String serviceName = dubboMetadataServiceURL.getParameter(APPLICATION_KEY);
String version = dubboMetadataServiceURL.getParameter(VERSION_KEY);
// Initialize DubboMetadataService with right version
return dubboMetadataConfigServiceProxy.initProxy(serviceName, version);
}
@Override
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
public List<URL> findSubscribedDubboMetadataServiceURLs(URL subscribedURL) {
// The parameter of "group" as the service name
String serviceInterface = subscribedURL.getServiceInterface();
String group = subscribedURL.getParameter(GROUP_KEY);
String version = subscribedURL.getParameter(VERSION_KEY);
String protocol = subscribedURL.getParameter(PROTOCOL_KEY);
List<ServiceInstance> serviceInstances = discoveryClient.getInstances(group);
List<URL> urls = dubboMetadataUtils.getDubboMetadataServiceURLs(serviceInstances,
serviceInterface, version, protocol);
if (logger.isInfoEnabled()) {
logger.info(
"The DubboMetadataService of service [name : {} , instances : {}] URLs[protocol : {} , size : {}] has been subscribed.",
group, serviceInstances.size(), protocol, urls.size());
}
return urls;
}
}

@ -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.metadata.repository;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
import org.springframework.cloud.client.ServiceInstance;
import static java.util.Optional.of;
import static org.springframework.util.CollectionUtils.isEmpty;
/**
* Random {@link ServiceInstanceSelector}.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class RandomServiceInstanceSelector implements ServiceInstanceSelector {
@Override
public Optional<ServiceInstance> select(List<ServiceInstance> serviceInstances) {
if (isEmpty(serviceInstances)) {
return Optional.empty();
}
ThreadLocalRandom random = ThreadLocalRandom.current();
return of(serviceInstances.get(random.nextInt(serviceInstances.size())));
}
}

@ -26,13 +26,13 @@ import org.springframework.cloud.client.ServiceInstance;
*
* @author <a href="mailto:liuxx-u@outlook.com">liuxx</a>
*/
public interface MetadataServiceInstanceSelector {
public interface ServiceInstanceSelector {
/**
* choose a service instance to get metadata.
* Select a service instance to get metadata.
* @param serviceInstances all service instance
* @return the service instance to get metadata
*/
Optional<ServiceInstance> choose(List<ServiceInstance> serviceInstances);
Optional<ServiceInstance> select(List<ServiceInstance> serviceInstances);
}

@ -63,6 +63,7 @@ import static org.springframework.util.StringUtils.hasText;
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Deprecated
public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
/**
@ -370,12 +371,7 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
}
private void subscribeDubboMetadataServiceURLs(URL url, NotifyListener listener) {
String serviceInterface = url.getServiceInterface();
String group = url.getParameter(GROUP_KEY);
String version = url.getParameter(VERSION_KEY);
String protocol = url.getParameter(PROTOCOL_KEY);
List<URL> urls = repository.findSubscribedDubboMetadataServiceURLs(
serviceInterface, group, version, protocol);
List<URL> urls = repository.findSubscribedDubboMetadataServiceURLs(url);
listener.notify(urls);
}

@ -0,0 +1,511 @@
/*
* 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;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.registry.event.ServiceInstancesChangedEvent;
import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory;
import com.alibaba.cloud.dubbo.service.DubboMetadataService;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy;
import com.alibaba.cloud.dubbo.util.DubboMetadataUtils;
import com.alibaba.cloud.dubbo.util.JSONUtils;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.URLBuilder;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.support.FailbackRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.CollectionUtils;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static java.util.stream.StreamSupport.stream;
import static org.apache.dubbo.common.URLBuilder.from;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PID_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE;
import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
import static org.apache.dubbo.common.utils.CollectionUtils.isEmpty;
import static org.apache.dubbo.registry.Constants.ADMIN_PROTOCOL;
import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URLS_PROPERTY_NAME;
import static org.springframework.util.StringUtils.hasText;
/**
* Dubbo Cloud {@link FailbackRegistry} is based on Spring Cloud {@link DiscoveryClient}.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboCloudRegistry extends FailbackRegistry {
/**
* The parameter name of {@link #servicesLookupInterval}.
*/
public static final String SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.services.lookup.interval";
protected static final String DUBBO_METADATA_SERVICE_CLASS_NAME = DubboMetadataService.class
.getName();
/**
* Caches the IDs of {@link ApplicationListener}.
*/
private static final Set<String> registerListeners = new HashSet<>();
protected final Logger logger = LoggerFactory.getLogger(getClass());
private final DiscoveryClient discoveryClient;
private final DubboServiceMetadataRepository repository;
private final DubboMetadataServiceProxy dubboMetadataConfigServiceProxy;
private final JSONUtils jsonUtils;
private final DubboGenericServiceFactory dubboGenericServiceFactory;
private final DubboMetadataUtils dubboMetadataUtils;
/**
* The interval in second of lookup service names(only for Dubbo-OPS).
*/
private final long servicesLookupInterval;
private final ConfigurableApplicationContext applicationContext;
private final String currentApplicationName;
public DubboCloudRegistry(URL url, DiscoveryClient discoveryClient,
DubboServiceMetadataRepository repository,
DubboMetadataServiceProxy dubboMetadataConfigServiceProxy,
JSONUtils jsonUtils, DubboGenericServiceFactory dubboGenericServiceFactory,
ConfigurableApplicationContext applicationContext) {
super(url);
this.servicesLookupInterval = url
.getParameter(SERVICES_LOOKUP_INTERVAL_PARAM_NAME, 60L);
this.discoveryClient = discoveryClient;
this.repository = repository;
this.dubboMetadataConfigServiceProxy = dubboMetadataConfigServiceProxy;
this.jsonUtils = jsonUtils;
this.dubboGenericServiceFactory = dubboGenericServiceFactory;
this.applicationContext = applicationContext;
this.dubboMetadataUtils = getBean(DubboMetadataUtils.class);
this.currentApplicationName = dubboMetadataUtils.getCurrentApplicationName();
}
private <T> T getBean(Class<T> beanClass) {
return this.applicationContext.getBean(beanClass);
}
protected boolean shouldRegister(URL url) {
String side = url.getParameter(SIDE_KEY);
boolean should = PROVIDER_SIDE.equals(side); // Only register the Provider.
if (!should) {
if (logger.isDebugEnabled()) {
logger.debug("The URL[{}] should not be registered.", url.toString());
}
}
return should;
}
@Override
public final void doRegister(URL url) {
if (!shouldRegister(url)) {
return;
}
repository.exportURL(url);
}
@Override
public final void doUnregister(URL url) {
if (!shouldRegister(url)) {
return;
}
repository.unexportURL(url);
}
@Override
public final void doSubscribe(URL url, NotifyListener listener) {
if (isAdminURL(url)) {
// TODO in future
if (logger.isWarnEnabled()) {
logger.warn("This feature about admin will be supported in the future.");
}
}
else if (isDubboMetadataServiceURL(url)) { // for DubboMetadataService
subscribeDubboMetadataServiceURLs(url, listener);
}
else { // for general Dubbo Services
subscribeURLs(url, listener);
}
}
private void subscribeURLs(URL url, NotifyListener listener) {
// Sync subscription
subscribeURLs(url, getServices(url), listener);
// Async subscription
registerServiceInstancesChangedListener(url, event -> {
Set<String> serviceNames = getServices(url);
String serviceName = event.getServiceName();
if (serviceNames.contains(serviceName)) {
subscribeURLs(url, serviceNames, listener);
}
});
}
private void subscribeURLs(URL url, Set<String> serviceNames,
NotifyListener listener) {
List<URL> subscribedURLs = new LinkedList<>();
serviceNames.forEach(serviceName -> {
subscribeURLs(url, subscribedURLs, serviceName,
() -> getServiceInstances(serviceName));
});
// Notify all
notifyAllSubscribedURLs(url, subscribedURLs, listener);
}
private void registerServiceInstancesChangedListener(URL url,
ApplicationListener<ServiceInstancesChangedEvent> listener) {
String listenerId = generateId(url);
if (registerListeners.add(listenerId)) {
applicationContext.addApplicationListener(listener);
}
}
private void subscribeURLs(URL subscribedURL, List<URL> subscribedURLs,
String serviceName,
Supplier<List<ServiceInstance>> serviceInstancesSupplier) {
List<ServiceInstance> serviceInstances = serviceInstancesSupplier.get();
subscribeURLs(subscribedURL, subscribedURLs, serviceName, serviceInstances);
}
private void subscribeURLs(URL subscribedURL, List<URL> subscribedURLs,
String serviceName, List<ServiceInstance> serviceInstances) {
if (CollectionUtils.isEmpty(serviceInstances)) {
if (logger.isWarnEnabled()) {
logger.warn(format("There is no instance in service[name : %s]",
serviceName));
}
}
List<URL> exportedURLs = getExportedURLs(subscribedURL, serviceName,
serviceInstances);
/**
* Add the exported URLs from {@link MetadataService}
*/
subscribedURLs.addAll(exportedURLs);
}
private List<URL> getExportedURLs(URL subscribedURL, String serviceName,
List<ServiceInstance> serviceInstances) {
List<ServiceInstance> validServiceInstances = filter(serviceInstances);
// If there is no valid ServiceInstance, return empty result
if (isEmpty(validServiceInstances)) {
if (logger.isWarnEnabled()) {
logger.warn(
"There is no instance from service[name : {}], and then Dubbo Service[key : {}] will not be "
+ "available , please make sure the further impact",
serviceName, subscribedURL.getServiceKey());
}
return emptyList();
}
List<URL> subscribedURLs = cloneExportedURLs(subscribedURL, serviceInstances);
// clear local service instances, help GC
validServiceInstances.clear();
return subscribedURLs;
}
/**
* Clone the subscribed URLs based on the template URLs.
* @param subscribedURL the URL to be subscribed
* @param serviceInstances the list of {@link ServiceInstance service instances}
* @return non-null
*/
private List<URL> cloneExportedURLs(URL subscribedURL,
List<ServiceInstance> serviceInstances) {
List<URL> clonedExportedURLs = new LinkedList<>();
serviceInstances.forEach(serviceInstance -> {
String host = serviceInstance.getHost();
getTemplateExportedURLs(subscribedURL, serviceInstances).stream()
.map(templateURL -> templateURL.removeParameter(TIMESTAMP_KEY))
.map(templateURL -> templateURL.removeParameter(PID_KEY))
.map(templateURL -> {
String protocol = templateURL.getProtocol();
int port = repository.getDubboProtocolPort(serviceInstance,
protocol);
if (Objects.equals(templateURL.getHost(), host)
&& Objects.equals(templateURL.getPort(), port)) { // use
// templateURL
// if
// equals
return templateURL;
}
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 clonedExportedURLs;
}
private List<URL> getTemplateExportedURLs(URL subscribedURL,
List<ServiceInstance> serviceInstances) {
DubboMetadataService dubboMetadataService = getProxy(serviceInstances);
List<URL> templateExportedURLs = emptyList();
if (dubboMetadataService != null) {
templateExportedURLs = getExportedURLs(dubboMetadataService, subscribedURL);
}
else {
if (logger.isWarnEnabled()) {
logger.warn(
"The metadata of Dubbo service[key : {}] still can't be found, it could effect the further "
+ "Dubbo service invocation",
subscribedURL.getServiceKey());
}
}
return templateExportedURLs;
}
private DubboMetadataService getProxy(List<ServiceInstance> serviceInstances) {
return dubboMetadataConfigServiceProxy.getProxy(serviceInstances);
}
private List<ServiceInstance> filter(Collection<ServiceInstance> serviceInstances) {
return serviceInstances.stream().filter(this::isDubboServiceInstance)
.collect(Collectors.toList());
}
private boolean isDubboServiceInstance(ServiceInstance serviceInstance) {
Map<String, String> metadata = serviceInstance.getMetadata();
return metadata.containsKey(METADATA_SERVICE_URLS_PROPERTY_NAME);
}
private Set<String> getServices(URL url) {
Set<String> subscribedServices = repository.getSubscribedServices();
// TODO Add the filter feature
return subscribedServices;
}
private void notifyAllSubscribedURLs(URL url, List<URL> subscribedURLs,
NotifyListener listener) {
if (isEmpty(subscribedURLs)) {
// Add the EMPTY_PROTOCOL URL
subscribedURLs.add(emptyURL(url));
if (isDubboMetadataServiceURL(url)) {
// if meta service change, and serviceInstances is zero, will clean up
// information about this client
String serviceName = url.getParameter(GROUP_KEY);
repository.removeMetadataAndInitializedService(serviceName, url);
}
}
if (logger.isDebugEnabled()) {
logger.debug("The subscribed URL[{}] will notify all URLs : {}", url,
subscribedURLs);
}
// Notify all
listener.notify(subscribedURLs);
}
private List<ServiceInstance> getServiceInstances(Iterable<String> serviceNames) {
return stream(serviceNames.spliterator(), false).map(this::getServiceInstances)
.flatMap(Collection::stream).collect(Collectors.toList());
}
private List<ServiceInstance> getServiceInstances(String serviceName) {
return hasText(serviceName) ? doGetServiceInstances(serviceName) : emptyList();
}
private List<ServiceInstance> doGetServiceInstances(String serviceName) {
List<ServiceInstance> serviceInstances = emptyList();
try {
serviceInstances = discoveryClient.getInstances(serviceName);
}
catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage(), e);
}
}
return serviceInstances;
}
private String generateId(URL url) {
return url.getServiceKey();
}
private URL emptyURL(URL url) {
// issue : When the last service provider is closed, the client still periodically
// connects to the last provider.n
// fix https://github.com/alibaba/spring-cloud-alibaba/issues/1259
return from(url).setProtocol(EMPTY_PROTOCOL).removeParameter(CATEGORY_KEY)
.build();
}
private List<URL> getExportedURLs(DubboMetadataService dubboMetadataService,
URL subscribedURL) {
String serviceInterface = subscribedURL.getServiceInterface();
String group = subscribedURL.getParameter(GROUP_KEY);
String version = subscribedURL.getParameter(VERSION_KEY);
// The subscribed protocol may be null
String subscribedProtocol = subscribedURL.getParameter(PROTOCOL_KEY);
String exportedURLsJSON = dubboMetadataService.getExportedURLs(serviceInterface,
group, version);
return jsonUtils.toURLs(exportedURLsJSON).stream()
.filter(exportedURL -> subscribedProtocol == null
|| subscribedProtocol.equalsIgnoreCase(exportedURL.getProtocol()))
.collect(Collectors.toList());
}
private void subscribeDubboMetadataServiceURLs(URL subscribedURL,
NotifyListener listener) {
// Sync subscription
subscribeDubboMetadataServiceURLs(subscribedURL, listener,
getServiceName(subscribedURL));
// Sync subscription
if (containsProviderCategory(subscribedURL)) {
registerServiceInstancesChangedListener(subscribedURL, event -> {
String sourceServiceName = event.getServiceName();
String serviceName = getServiceName(subscribedURL);
if (Objects.equals(sourceServiceName, serviceName)) {
subscribeDubboMetadataServiceURLs(subscribedURL, listener,
sourceServiceName);
}
});
}
}
private String getServiceName(URL subscribedURL) {
return subscribedURL.getParameter(GROUP_KEY);
}
private void subscribeDubboMetadataServiceURLs(URL subscribedURL,
NotifyListener listener, String serviceName) {
String serviceInterface = subscribedURL.getServiceInterface();
String version = subscribedURL.getParameter(VERSION_KEY);
String protocol = subscribedURL.getParameter(PROTOCOL_KEY);
List<ServiceInstance> serviceInstances = getServiceInstances(serviceName);
List<URL> urls = dubboMetadataUtils.getDubboMetadataServiceURLs(serviceInstances,
serviceInterface, version, protocol);
notifyAllSubscribedURLs(subscribedURL, urls, listener);
}
// private void subscribeDubboMetadataServiceURLs(URL subscribedURL,
// NotifyListener listener, Set<String> serviceNames) {
//
// String serviceInterface = subscribedURL.getServiceInterface();
// String version = subscribedURL.getParameter(VERSION_KEY);
// String protocol = subscribedURL.getParameter(PROTOCOL_KEY);
//
// List<ServiceInstance> serviceInstances = getServiceInstances(serviceNames);
//
// List<URL> urls = dubboMetadataUtils.getDubboMetadataServiceURLs(serviceInstances,
// serviceInterface, version, protocol);
//
// notifyAllSubscribedURLs(subscribedURL, urls, listener);
// }
private boolean containsProviderCategory(URL subscribedURL) {
String category = subscribedURL.getParameter(CATEGORY_KEY);
return category == null ? false : category.contains(PROVIDER);
}
@Override
public final void doUnsubscribe(URL url, NotifyListener listener) {
// TODO
}
@Override
public boolean isAvailable() {
return !discoveryClient.getServices().isEmpty();
}
protected boolean isAdminURL(URL url) {
return ADMIN_PROTOCOL.equals(url.getProtocol());
}
protected boolean isDubboMetadataServiceURL(URL url) {
return DUBBO_METADATA_SERVICE_CLASS_NAME.equals(url.getServiceInterface());
}
}

@ -31,7 +31,10 @@ import org.springframework.context.ConfigurableApplicationContext;
* protocol is "spring-cloud".
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @deprecated It's a legacy and not recommended implementation, being replacing to be
* {@link DubboCloudRegistry}
*/
@Deprecated
public class SpringCloudRegistry extends AbstractSpringCloudRegistry {
private final DubboServiceMetadataRepository dubboServiceMetadataRepository;

@ -16,6 +16,7 @@
package com.alibaba.cloud.dubbo.registry;
import com.alibaba.cloud.dubbo.env.DubboCloudProperties;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy;
@ -28,7 +29,7 @@ import org.apache.dubbo.registry.support.AbstractRegistryFactory;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.ConfigurableApplicationContext;
import static java.lang.System.getProperty;
import static com.alibaba.cloud.dubbo.util.DubboCloudConstants.SPRING_CLOUD_REGISTRY_PROPERTY_VALUE;
/**
* Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose
@ -50,10 +51,6 @@ public class SpringCloudRegistryFactory extends AbstractRegistryFactory {
*/
public static String ADDRESS = "localhost";
private static String SERVICES_LOOKUP_SCHEDULER_THREAD_NAME_PREFIX = getProperty(
"dubbo.services.lookup.scheduler.thread.name.prefix ",
"dubbo-services-lookup-");
private static ConfigurableApplicationContext applicationContext;
private DiscoveryClient discoveryClient;
@ -88,9 +85,26 @@ public class SpringCloudRegistryFactory extends AbstractRegistryFactory {
@Override
public Registry createRegistry(URL url) {
init();
return new SpringCloudRegistry(url, discoveryClient,
dubboServiceMetadataRepository, dubboMetadataConfigServiceProxy,
jsonUtils, dubboGenericServiceFactory, applicationContext);
DubboCloudProperties dubboCloudProperties = applicationContext
.getBean(DubboCloudProperties.class);
Registry registry = null;
switch (dubboCloudProperties.getRegistryType()) {
case SPRING_CLOUD_REGISTRY_PROPERTY_VALUE:
registry = new SpringCloudRegistry(url, discoveryClient,
dubboServiceMetadataRepository, dubboMetadataConfigServiceProxy,
jsonUtils, dubboGenericServiceFactory, applicationContext);
break;
default:
registry = new DubboCloudRegistry(url, discoveryClient,
dubboServiceMetadataRepository, dubboMetadataConfigServiceProxy,
jsonUtils, dubboGenericServiceFactory, applicationContext);
break;
}
return registry;
}
}

@ -16,14 +16,14 @@
package com.alibaba.cloud.dubbo.registry.event;
import java.util.Collection;
import java.util.List;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import static java.util.Collections.unmodifiableCollection;
import static java.util.Collections.unmodifiableList;
/**
* An event raised after the {@link ServiceInstance instances} of one service has been
@ -35,7 +35,7 @@ public class ServiceInstancesChangedEvent extends ApplicationEvent {
private final String serviceName;
private final Collection<ServiceInstance> serviceInstances;
private final List<ServiceInstance> serviceInstances;
/**
* Current event has been processed or not. Typically, Spring Event was based on sync
@ -51,10 +51,10 @@ public class ServiceInstancesChangedEvent extends ApplicationEvent {
* @throws IllegalArgumentException if source is null.
*/
public ServiceInstancesChangedEvent(String serviceName,
Collection<ServiceInstance> serviceInstances) {
List<ServiceInstance> serviceInstances) {
super(serviceName);
this.serviceName = serviceName;
this.serviceInstances = unmodifiableCollection(serviceInstances);
this.serviceInstances = unmodifiableList(serviceInstances);
}
/**
@ -67,7 +67,7 @@ public class ServiceInstancesChangedEvent extends ApplicationEvent {
/**
* @return all {@link ServiceInstance service instances}.
*/
public Collection<ServiceInstance> getServiceInstances() {
public List<ServiceInstance> getServiceInstances() {
return serviceInstances;
}

@ -95,10 +95,9 @@ public class DubboGenericServiceFactory {
private ReferenceBean<GenericService> build(String interfaceName, String version,
String group, Map<String, Object> dubboTranslatedAttributes) {
Integer key = Objects.hash(interfaceName, version, group,
dubboTranslatedAttributes);
String key = createKey(interfaceName, version, group, dubboTranslatedAttributes);
return cache.computeIfAbsent(group + key, k -> {
return cache.computeIfAbsent(key, k -> {
ReferenceBean<GenericService> referenceBean = new ReferenceBean<>();
referenceBean.setGeneric(true);
referenceBean.setInterface(interfaceName);
@ -110,6 +109,12 @@ public class DubboGenericServiceFactory {
});
}
private String createKey(String interfaceName, String version, String group,
Map<String, Object> dubboTranslatedAttributes) {
return group + "#"
+ Objects.hash(interfaceName, version, group, dubboTranslatedAttributes);
}
private void bindReferenceBean(ReferenceBean<GenericService> referenceBean,
Map<String, Object> dubboTranslatedAttributes) {
DataBinder dataBinder = new DataBinder(referenceBean);
@ -155,7 +160,7 @@ public class DubboGenericServiceFactory {
cache.clear();
}
public synchronized void destroy(String serviceName) {
public void destroy(String serviceName) {
Set<String> removeGroups = new HashSet<>(cache.keySet());
for (String key : removeGroups) {
if (key.contains(serviceName)) {

@ -16,13 +16,25 @@
package com.alibaba.cloud.dubbo.service;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import com.alibaba.cloud.dubbo.metadata.repository.ServiceInstanceSelector;
import com.alibaba.cloud.dubbo.util.DubboMetadataUtils;
import org.apache.dubbo.common.URL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import static java.lang.reflect.Proxy.newProxyInstance;
import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
/**
* The proxy of {@link DubboMetadataService}.
@ -31,34 +43,110 @@ import static java.lang.reflect.Proxy.newProxyInstance;
*/
public class DubboMetadataServiceProxy implements BeanClassLoaderAware, DisposableBean {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final DubboGenericServiceFactory dubboGenericServiceFactory;
private final DubboMetadataUtils dubboMetadataUtils;
private final ServiceInstanceSelector serviceInstanceSelector;
private final DiscoveryClient discoveryClient;
private final Map<String, DubboMetadataService> dubboMetadataServiceCache = new ConcurrentHashMap<>();
private ClassLoader classLoader;
public DubboMetadataServiceProxy(
DubboGenericServiceFactory dubboGenericServiceFactory) {
DubboGenericServiceFactory dubboGenericServiceFactory,
DubboMetadataUtils dubboMetadataUtils,
ServiceInstanceSelector serviceInstanceSelector,
DiscoveryClient discoveryClient) {
this.dubboGenericServiceFactory = dubboGenericServiceFactory;
this.dubboMetadataUtils = dubboMetadataUtils;
this.serviceInstanceSelector = serviceInstanceSelector;
this.discoveryClient = discoveryClient;
}
/**
* Initializes {@link DubboMetadataService}'s Proxy.
* Remove {@link DubboMetadataService}'s Proxy by service name.
* @param serviceName the service name
* @param version the service version
*/
public void removeProxy(String serviceName) {
dubboMetadataServiceCache.remove(serviceName);
dubboGenericServiceFactory.destroy(serviceName);
}
/**
* Get the proxy of {@link DubboMetadataService} if possible.
* @param serviceInstances the instances of {@link DubboMetadataService}
* @return <code>null</code> if initialization can't be done
*/
public DubboMetadataService getProxy(List<ServiceInstance> serviceInstances) {
DubboMetadataService dubboMetadataService = null;
// attempt to get the proxy of DubboMetadataService in maximum times
int attempts = serviceInstances.size();
for (int i = 0; i < attempts; i++) {
Optional<ServiceInstance> serviceInstance = select(serviceInstances);
if (serviceInstance.isPresent()) {
List<URL> dubboMetadataServiceURLs = getDubboMetadataServiceURLs(
serviceInstance.get());
for (URL dubboMetadataServiceURL : dubboMetadataServiceURLs) {
dubboMetadataService = createProxyIfAbsent(dubboMetadataServiceURL);
if (dubboMetadataService != null) {
return dubboMetadataService;
}
}
}
}
return dubboMetadataService;
}
/**
* Is the {@link DubboMetadataService}'s Proxy initialized or not.
* @param serviceName the service name
* @return <code>true</code> if initialized , or return <code>false</code>
*/
public boolean isInitialized(String serviceName) {
return dubboMetadataServiceCache.containsKey(serviceName);
}
/**
* Create a {@link DubboMetadataService}'s Proxy If abstract.
* @param dubboMetadataServiceURL the {@link URL} of {@link DubboMetadataService}
* @return a {@link DubboMetadataService} proxy
*/
public DubboMetadataService initProxy(String serviceName, String version) {
return dubboMetadataServiceCache.computeIfAbsent(serviceName,
name -> newProxy(name, version));
private DubboMetadataService createProxyIfAbsent(URL dubboMetadataServiceURL) {
String serviceName = dubboMetadataServiceURL.getParameter(APPLICATION_KEY);
String version = dubboMetadataServiceURL.getParameter(VERSION_KEY);
// Initialize DubboMetadataService with right version
return createProxyIfAbsent(serviceName, version);
}
/**
* Remove {@link DubboMetadataService}'s Proxy by service name.
* Initializes {@link DubboMetadataService}'s Proxy.
* @param serviceName the service name
* @param version the service version
* @return a {@link DubboMetadataService} proxy
*/
public void removeProxy(String serviceName) {
dubboMetadataServiceCache.remove(serviceName);
private DubboMetadataService createProxyIfAbsent(String serviceName, String version) {
return dubboMetadataServiceCache.computeIfAbsent(serviceName,
name -> createProxy(name, version));
}
private Optional<ServiceInstance> select(List<ServiceInstance> serviceInstances) {
return serviceInstanceSelector.select(serviceInstances);
}
private List<URL> getDubboMetadataServiceURLs(ServiceInstance serviceInstance) {
return dubboMetadataUtils.getDubboMetadataServiceURLs(serviceInstance);
}
/**
@ -68,7 +156,12 @@ public class DubboMetadataServiceProxy implements BeanClassLoaderAware, Disposab
* @return a {@link DubboMetadataService} proxy
*/
public DubboMetadataService getProxy(String serviceName) {
return dubboMetadataServiceCache.get(serviceName);
return dubboMetadataServiceCache.getOrDefault(serviceName,
getProxy0(serviceName));
}
private DubboMetadataService getProxy0(String serviceName) {
return getProxy(discoveryClient.getInstances(serviceName));
}
@Override
@ -88,7 +181,14 @@ public class DubboMetadataServiceProxy implements BeanClassLoaderAware, Disposab
* @param version the service version
* @return a {@link DubboMetadataService} proxy
*/
protected DubboMetadataService newProxy(String serviceName, String version) {
protected DubboMetadataService createProxy(String serviceName, String version) {
if (logger.isInfoEnabled()) {
logger.info(
"The metadata of Dubbo service[name : {}] is about to be initialized",
serviceName);
}
return (DubboMetadataService) newProxyInstance(classLoader,
new Class[] { DubboMetadataService.class },
new DubboMetadataServiceInvocationHandler(serviceName, version,

@ -0,0 +1,51 @@
/*
* 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.util;
/**
* The constants for Dubbo Spring Cloud.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public final class DubboCloudConstants {
/**
* The property prefix of Configuration.
*/
public static final String CONFIG_PROPERTY_PREFIX = "dubbo.cloud";
/**
* The property name of Registry type.
*/
public static final String REGISTRY_TYPE_PROPERTY_NAME = CONFIG_PROPERTY_PREFIX
+ ".registry-type";
/**
* The property value of Spring Cloud Registry.
*/
public static final String SPRING_CLOUD_REGISTRY_PROPERTY_VALUE = "spring-cloud";
/**
* The property value of Dubbo Cloud Registry.
*/
public static final String DUBBO_CLOUD_REGISTRY_PROPERTY_VALUE = "dubbo-cloud";
private DubboCloudConstants() {
throw new AssertionError("Must not instantiate constant utility class");
}
}

@ -0,0 +1,118 @@
/*
* 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.util;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import com.alibaba.cloud.dubbo.service.DubboMetadataService;
import org.apache.dubbo.common.URL;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.core.env.Environment;
import static java.lang.String.format;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URLS_PROPERTY_NAME;
import static org.springframework.util.StringUtils.hasText;
/**
* The utilities class of Dubbo Metadata.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboMetadataUtils {
/**
* The {@link String#format(String, Object...) pattern} of dubbo protocols port.
*/
public static final String DUBBO_PROTOCOLS_PORT_PROPERTY_NAME_PATTERN = "dubbo.protocols.%s.port";
private final JSONUtils jsonUtils;
private final Environment environment;
private final String currentApplicationName;
public DubboMetadataUtils(JSONUtils jsonUtils, Environment environment) {
this.jsonUtils = jsonUtils;
this.environment = environment;
this.currentApplicationName = environment.getProperty("spring.application.name",
environment.getProperty("dubbo.application.name", "application"));
}
/**
* Get the current application name.
* @return non-null
*/
public String getCurrentApplicationName() {
return currentApplicationName;
}
/**
* Get the {@link URL urls} that {@link DubboMetadataService} exported by the
* specified {@link ServiceInstance}.
* @param serviceInstance {@link ServiceInstance}
* @return the mutable {@link URL urls}
*/
public List<URL> getDubboMetadataServiceURLs(ServiceInstance serviceInstance) {
Map<String, String> metadata = serviceInstance.getMetadata();
String dubboURLsJSON = metadata.get(METADATA_SERVICE_URLS_PROPERTY_NAME);
return jsonUtils.toURLs(dubboURLsJSON);
}
/**
* Get the {@link URL urls} that {@link DubboMetadataService} exported by the
* specified {@link ServiceInstance ServiceInstances}.
* @param serviceInstances the list of {@link ServiceInstance ServiceInstances}
* @param serviceInterface the interface name of Dubbo service
* @param version the version of Dubbo service
* @param protocol the protocol that Dubbo Service exports
* @return the mutable {@link URL urls}
*/
public List<URL> getDubboMetadataServiceURLs(List<ServiceInstance> serviceInstances,
String serviceInterface, String version, String protocol) {
return serviceInstances.stream().map(this::getDubboMetadataServiceURLs)
.flatMap(List::stream)
.filter(url -> protocol == null
|| Objects.equals(protocol, url.getProtocol()))
.filter(url -> Objects.equals(serviceInterface,
url.getServiceInterface()))
.filter(url -> Objects.equals(version, url.getParameter(VERSION_KEY)))
.collect(Collectors.toList());
}
/**
* Get the property name of Dubbo Protocol.
* @param protocol Dubbo Protocol
* @return non-null
*/
public String getDubboProtocolPropertyName(String protocol) {
return format(DUBBO_PROTOCOLS_PORT_PROPERTY_NAME_PATTERN, protocol);
}
public Integer getDubboProtocolPort(ServiceInstance serviceInstance,
String protocol) {
String protocolProperty = getDubboProtocolPropertyName(protocol);
Map<String, String> metadata = serviceInstance.getMetadata();
String protocolPort = metadata.get(protocolProperty);
return hasText(protocolPort) ? Integer.valueOf(protocolPort) : null;
}
}

@ -1,7 +1,6 @@
# Dubbo Endpoints Default Properties is loaded by @PropertySource with low order,
# Set enabled for Dubbo Endpoints
management.endpoint.dubborestmetadata.enabled = true
management.endpoint.dubborestmetadata.enabled=true
# "management.endpoints.web.base-path" should not be configured in this file
# Re-defines path-mapping of Dubbo Web Endpoints
management.endpoints.web.path-mapping.dubborestmetadata = dubbo/rest/metadata
management.endpoints.web.path-mapping.dubborestmetadata=dubbo/rest/metadata

@ -6,12 +6,9 @@ com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationNonWebApplicationA
com.alibaba.cloud.dubbo.autoconfigure.DubboLoadBalancedRestTemplateAutoConfiguration,\
com.alibaba.cloud.dubbo.autoconfigure.DubboServiceAutoConfiguration,\
com.alibaba.cloud.dubbo.autoconfigure.DubboServiceDiscoveryAutoConfiguration
org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration=\
com.alibaba.cloud.dubbo.actuate.DubboMetadataEndpointAutoConfiguration
org.springframework.context.ApplicationContextInitializer=\
com.alibaba.cloud.dubbo.context.DubboServiceRegistrationApplicationContextInitializer
org.springframework.boot.env.EnvironmentPostProcessor=\
com.alibaba.cloud.dubbo.env.DubboNonWebApplicationEnvironmentPostProcessor
Loading…
Cancel
Save