fix(nacos): fix issue #859

pull/920/head
chuntaojun 5 years ago
commit f0f86868b2

@ -108,7 +108,7 @@ Examples
[Alibaba Cloud OSS Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/oss-example/readme.md)
[Duboo Spring Cloud Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/README_CN.md)
[Dubbo Spring Cloud Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/README_CN.md)
## Version control guidelines
The version number of the project is in the form of x.x.x, where x is a number, starting from 0, and is not limited to the range 0~9. When the project is in the incubator phase, the version number is 0.x.x.

@ -8,13 +8,13 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>2.1.6.RELEASE</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath/>
</parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Cloud Alibaba</name>
<description>Spring Cloud Alibaba</description>
@ -45,8 +45,10 @@
<email>flystar32@163.com</email>
</developer>
<developer>
<name>fangjian</name>
<name>Jim Fang</name>
<email>fangjian0423@gmail.com</email>
<organization>Alibaba</organization>
<url>https://github.com/fangjian0423</url>
</developer>
<developer>
<name>xiaolongzuo</name>
@ -71,16 +73,23 @@
<properties>
<!-- Dependency Versions -->
<spring-cloud-commons.version>2.1.2.RELEASE</spring-cloud-commons.version>
<spring-cloud-netflix.version>2.1.2.RELEASE</spring-cloud-netflix.version>
<spring-cloud-openfeign.version>2.1.2.RELEASE</spring-cloud-openfeign.version>
<spring-cloud-bus.version>2.1.2.RELEASE</spring-cloud-bus.version>
<spring-cloud-gateway.version>2.1.2.RELEASE</spring-cloud-gateway.version>
<spring-cloud-commons.version>2.2.0.BUILD-SNAPSHOT</spring-cloud-commons.version>
<spring-cloud-netflix.version>2.2.0.BUILD-SNAPSHOT</spring-cloud-netflix.version>
<spring-cloud-openfeign.version>2.2.0.BUILD-SNAPSHOT</spring-cloud-openfeign.version>
<spring-cloud-bus.version>2.2.0.BUILD-SNAPSHOT</spring-cloud-bus.version>
<spring-cloud-gateway.version>2.2.0.BUILD-SNAPSHOT</spring-cloud-gateway.version>
<spring-cloud-stream.version>Horsham.BUILD-SNAPSHOT</spring-cloud-stream.version>
<junit.version>4.12</junit.version>
<javax-servlet-api>3.0</javax-servlet-api>
<slf4j-api.version>1.7.25</slf4j-api.version>
<!-- Apache Dubbo -->
<dubbo-spring-boot.version>2.7.1</dubbo-spring-boot.version>
<dubbo.version>2.7.3</dubbo.version>
<!-- Apache RocketMQ -->
<rocketmq.starter.version>2.0.2</rocketmq.starter.version>
<!-- Maven Plugin Versions -->
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
@ -95,6 +104,7 @@
<module>spring-cloud-alibaba-sentinel</module>
<module>spring-cloud-alibaba-sentinel-datasource</module>
<module>spring-cloud-alibaba-sentinel-gateway</module>
<module>spring-cloud-circuitbreaker-sentinel</module>
<module>spring-cloud-alibaba-nacos-config</module>
<module>spring-cloud-alibaba-nacos-discovery</module>
<module>spring-cloud-alibaba-seata</module>
@ -180,6 +190,46 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-dependencies</artifactId>
<version>${spring-cloud-stream.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo-spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>${rocketmq.starter.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -13,7 +13,7 @@
<name>Spring Cloud Alibaba Coverage</name>
<properties>
<spring.cloud.alibaba.version>2.1.1.BUILD-SNAPSHOT</spring.cloud.alibaba.version>
<spring.cloud.alibaba.version>2.2.0.BUILD-SNAPSHOT</spring.cloud.alibaba.version>
</properties>
<dependencies>
<dependency>

@ -6,13 +6,13 @@
<parent>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>2.1.6.RELEASE</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath/>
</parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Cloud Alibaba Dependencies</name>
<description>Spring Cloud Alibaba Dependencies</description>
@ -28,11 +28,8 @@
<aliyun.sdk.version>4.4.1</aliyun.sdk.version>
<alicloud.context.version>1.0.5</alicloud.context.version>
<aliyun.sdk.edas.version>2.44.0</aliyun.sdk.edas.version>
<rocketmq.starter.version>2.0.2</rocketmq.starter.version>
<schedulerX.client.version>2.1.6</schedulerX.client.version>
<dubbo.version>2.7.3</dubbo.version>
<dubbo-spring-boot.version>2.7.1</dubbo-spring-boot.version>
<dubbo-registry-nacos.version>2.7.1</dubbo-registry-nacos.version>
<aliyun.java.sdk.dysmsapi>1.1.0</aliyun.java.sdk.dysmsapi>
<aliyun.sdk.mns>1.1.8.6</aliyun.sdk.mns>
<aliyun.java.sdk.dyvmsapi>1.1.1</aliyun.java.sdk.dyvmsapi>
@ -105,13 +102,6 @@
<version>${nacos.config.version}</version>
</dependency>
<!-- Apache RocketMQ -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>${rocketmq.starter.version}</version>
</dependency>
<!-- Sentinel -->
<dependency>
<groupId>com.alibaba.csp</groupId>
@ -183,6 +173,11 @@
<artifactId>sentinel-apache-dubbo-adapter</artifactId>
<version>${sentinel.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-reactor-adapter</artifactId>
<version>${sentinel.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>sentinel-dubbo-api</artifactId>
@ -211,48 +206,12 @@
<!--Alibaba Seata-->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>${seata.version}</version>
</dependency>
<!-- Dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Dubbo Spring Boot Starter -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo-spring-boot.version}</version>
</dependency>
<!-- Dubbo Nacos registry dependency -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>${dubbo-registry-nacos.version}</version>
</dependency>
<!-- Aliyun OSS dependencies -->
<dependency>
<groupId>com.aliyun.oss</groupId>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -166,7 +166,7 @@ NOTE: 在使用 RocketMQ Binder 的同时也可以配置 rocketmq.** 用于触
MessageBuilder builder = MessageBuilder.withPayload(msg)
.setHeader(RocketMQHeaders.TAGS, "binder")
.setHeader(RocketMQHeaders.KEYS, "my-key")
.setHeader("DELAY", "1");
.setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, "1");
Message message = builder.build();
output().send(message);
```

@ -0,0 +1,73 @@
=== Circuit Breaker: Spring Cloud Circuit Breaker With Sentinel & Configuring Sentinel Circuit Breakers
==== Default Configuration
To provide a default configuration for all of your circuit breakers create a `Customizer` bean that is passed a
`SentinelCircuitBreakerFactory` or `ReactiveSentinelCircuitBreakerFactory`.
The `configureDefault` method can be used to provide a default configuration.
====
[source,java]
----
@Bean
public Customizer<SentinelCircuitBreakerFactory> defaultCustomizer() {
return factory -> factory.configureDefault(id -> new SentinelConfigBuilder(id)
.build());
}
----
====
You can choose to provide default circuit breaking rules via `SentinelConfigBuilder#rules(rules)`.
You can also choose to load circuit breaking rules later elsewhere using
`DegradeRuleManager.loadRules(rules)` API of Sentinel, or via Sentinel dashboard.
===== Reactive Example
====
[source,java]
----
@Bean
public Customizer<ReactiveSentinelCircuitBreakerFactory> defaultCustomizer() {
return factory -> factory.configureDefault(id -> new SentinelConfigBuilder(id)
.build());
}
----
====
==== Specific Circuit Breaker Configuration
Similarly to providing a default configuration, you can create a `Customizer` bean this is passed a
`SentinelCircuitBreakerFactory`.
====
[source,java]
----
@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)
);
return factory -> factory.configure(builder -> builder.rules(rules), slowId);
}
----
====
===== Reactive Example
====
[source,java]
----
@Bean
public Customizer<ReactiveSentinelCircuitBreakerFactory> customizer() {
List<DegradeRule> rules = Collections.singletonList(
new DegradeRule().setGrade(RuleConstant.DEGRADE_GRADE_RT)
.setCount(100)
.setTimeWindow(10)
);
return factory -> factory.configure(builder -> builder.rules(rules), "foo", "bar");
}
----
====

@ -114,7 +114,7 @@ All the message types in this code are provided by the `spring-messaging`module.
**The lower layer of Spring Cloud Stream also implements various code abstractions based on the previous code.**
=== How to use Spring Cloud Alibaba RocketMQ Binder ###
=== How to use Spring Cloud Alibaba RocketMQ Binder
For using the Spring Cloud Alibaba RocketMQ Binder, you just need to add it to your Spring Cloud Stream application, using the following Maven coordinates:
@ -165,7 +165,7 @@ For example, `TAGS`, `DELAY`, `TRANSACTIONAL_ARG`, `KEYS`, `WAIT_STORE_MSG_OK`,
MessageBuilder builder = MessageBuilder.withPayload(msg)
.setHeader(RocketMQHeaders.TAGS, "binder")
.setHeader(RocketMQHeaders.KEYS, "my-key")
.setHeader("DELAY", "1");
.setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, "1");
Message message = builder.build();
output().send(message);
```

@ -255,7 +255,7 @@ To learn more about how dynamic data sources work in Sentinel, refer to https://
=== Support Zuul
https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81[参考 Sentinel 网关限流]
Refer https://github.com/alibaba/Sentinel/wiki/API-Gateway-Flow-Control[API Gateway Flow Control]
If you want to use Sentinel Starter with Zuul, you need to add the `spring-cloud-alibaba-sentinel-gateway` dependency, and you need to add the `spring-cloud-starter-netflix-zuul` dependency to let Zuul AutoConfiguration class in the gateway module takes effect:
@ -278,7 +278,7 @@ If you want to use Sentinel Starter with Zuul, you need to add the `spring-cloud
=== Support Spring Cloud Gateway
https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81[参考 Sentinel 网关限流]
Refer https://github.com/alibaba/Sentinel/wiki/API-Gateway-Flow-Control[API Gateway Flow Control]
If you want to use Sentinel Starter with Spring Cloud Gateway, you need to add the `spring-cloud-alibaba-sentinel-gateway` dependency and add the `spring-cloud-starter-gateway` dependency to let Spring Cloud Gateway AutoConfiguration class in the module takes effect:
@ -299,6 +299,8 @@ If you want to use Sentinel Starter with Spring Cloud Gateway, you need to add t
</dependency>
```
include::circuitbreaker-sentinel.adoc[]
=== Sentinel Endpoint
Sentinel provides an Endpoint internally with a corresponding endpoint id of `sentinel`.

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -14,10 +14,8 @@
<name>Spring Cloud Alibaba Dubbo</name>
<properties>
<dubbo.version>2.7.3</dubbo.version>
<dubbo-spring-boot.version>2.7.1</dubbo-spring-boot.version>
<spring-cloud-zookeeper.version>2.1.2.RELEASE</spring-cloud-zookeeper.version>
<spring-cloud-consul.version>2.1.2.RELEASE</spring-cloud-consul.version>
<spring-cloud-zookeeper.version>2.2.0.BUILD-SNAPSHOT</spring-cloud-zookeeper.version>
<spring-cloud-consul.version>2.2.0.BUILD-SNAPSHOT</spring-cloud-consul.version>
<curator.version>4.0.1</curator.version>
</properties>
@ -158,28 +156,12 @@
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Dubbo Spring Boot Starter -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo-spring-boot.version}</version>
</dependency>
<!-- Dubbo Spring Boot Actuator (optional) -->

@ -25,16 +25,19 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
import org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.env.Environment;
import org.springframework.core.type.MethodMetadata;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
@ -56,8 +59,8 @@ import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory;
@ConditionalOnClass(name = { "org.springframework.web.client.RestTemplate" })
@AutoConfigureAfter(name = {
"org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration" })
public class DubboLoadBalancedRestTemplateAutoConfiguration
implements BeanClassLoaderAware, SmartInitializingSingleton {
public class DubboLoadBalancedRestTemplateAutoConfiguration implements
BeanClassLoaderAware, ApplicationContextAware, SmartInitializingSingleton {
private static final Class<DubboTransported> DUBBO_TRANSPORTED_CLASS = DubboTransported.class;
@ -89,6 +92,9 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration
@Autowired(required = false)
private Map<String, RestTemplate> restTemplates = Collections.emptyMap();
@Nullable
private ApplicationContext applicationContext;
private ClassLoader classLoader;
/**
@ -111,18 +117,21 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration
* {@link SmartInitializingSingleton} beans or
* {@link RestTemplateCustomizer#customize(RestTemplate) customization})
*/
@EventListener(ApplicationStartedEvent.class)
public void adaptRestTemplates() {
DubboTransportedAttributesResolver attributesResolver = new DubboTransportedAttributesResolver(
environment);
for (Map.Entry<String, RestTemplate> entry : restTemplates.entrySet()) {
String beanName = entry.getKey();
Map<String, Object> dubboTranslatedAttributes = getDubboTranslatedAttributes(
beanName, attributesResolver);
if (!CollectionUtils.isEmpty(dubboTranslatedAttributes)) {
adaptRestTemplate(entry.getValue(), dubboTranslatedAttributes);
@EventListener(ContextRefreshedEvent.class)
public void adaptRestTemplates(ContextRefreshedEvent event) {
if (event.getApplicationContext() == this.applicationContext) {
DubboTransportedAttributesResolver attributesResolver = new DubboTransportedAttributesResolver(
environment);
for (Map.Entry<String, RestTemplate> entry : restTemplates.entrySet()) {
String beanName = entry.getKey();
Map<String, Object> dubboTranslatedAttributes = getDubboTranslatedAttributes(
beanName, attributesResolver);
if (!CollectionUtils.isEmpty(dubboTranslatedAttributes)) {
adaptRestTemplate(entry.getValue(), dubboTranslatedAttributes);
}
}
}
}
@ -132,7 +141,7 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration
* {@link DubboTransported @DubboTransported}
*
* @param beanName the bean name of {@link LoadBalanced @LoadBalanced}
* {@link RestTemplate}
* {@link RestTemplate}
* @param attributesResolver {@link DubboTransportedAttributesResolver}
* @return non-null {@link Map}
*/
@ -144,10 +153,10 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration
AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
MethodMetadata factoryMethodMetadata = annotatedBeanDefinition
.getFactoryMethodMetadata();
attributes = factoryMethodMetadata != null
? Optional.ofNullable(factoryMethodMetadata
.getAnnotationAttributes(DUBBO_TRANSPORTED_CLASS_NAME)).orElse(attributes)
: Collections.emptyMap();
attributes = factoryMethodMetadata != null ? Optional
.ofNullable(factoryMethodMetadata
.getAnnotationAttributes(DUBBO_TRANSPORTED_CLASS_NAME))
.orElse(attributes) : Collections.emptyMap();
}
return attributesResolver.resolve(attributes);
}
@ -158,8 +167,8 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration
*
* @param restTemplate {@link LoadBalanced @LoadBalanced} {@link RestTemplate} Bean
* @param dubboTranslatedAttributes the annotation dubboTranslatedAttributes
* {@link RestTemplate} bean being annotated
* {@link DubboTransported @DubboTransported}
* {@link RestTemplate} bean being annotated
* {@link DubboTransported @DubboTransported}
*/
private void adaptRestTemplate(RestTemplate restTemplate,
Map<String, Object> dubboTranslatedAttributes) {
@ -188,4 +197,9 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration
this.classLoader = classLoader;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}

@ -16,6 +16,7 @@
package com.alibaba.cloud.dubbo.autoconfigure;
import java.util.Collection;
import java.util.Optional;
import java.util.function.Supplier;
import org.apache.dubbo.config.ProtocolConfig;
@ -30,9 +31,11 @@ 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;
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.resolver.DubboServiceBeanMetadataResolver;
import com.alibaba.cloud.dubbo.metadata.resolver.MetadataResolver;
import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory;
@ -68,6 +71,14 @@ public class DubboMetadataAutoConfiguration {
return new DubboServiceBeanMetadataResolver(contract);
}
@Bean
@ConditionalOnMissingBean
public MetadataServiceInstanceSelector metadataServiceInstanceSelector() {
return serviceInstances -> CollectionUtils.isEmpty(serviceInstances)
? Optional.empty()
: serviceInstances.stream().findAny();
}
@Bean
public Supplier<ProtocolConfig> dubboProtocolConfigSupplier(
ObjectProvider<Collection<ProtocolConfig>> protocols) {

@ -36,7 +36,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -50,7 +49,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
@ -168,7 +166,7 @@ public class DubboServiceMetadataRepository
private DiscoveryClient discoveryClient;
@Autowired
private LoadBalancerClient loadBalancerClient;
private MetadataServiceInstanceSelector metadataServiceInstanceSelector;
@Autowired
private JSONUtils jsonUtils;
@ -623,7 +621,7 @@ public class DubboServiceMetadataRepository
}
protected void initSubscribedDubboMetadataService(String serviceName) {
Optional.ofNullable(loadBalancerClient.choose(serviceName))
metadataServiceInstanceSelector.choose(discoveryClient.getInstances(serviceName))
.map(this::getDubboMetadataServiceURLs)
.ifPresent(dubboMetadataServiceURLs -> {
dubboMetadataServiceURLs.forEach(dubboMetadataServiceURL -> {

@ -0,0 +1,37 @@
/*
* Copyright (C) 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 org.springframework.cloud.client.ServiceInstance;
/**
* metadata service instance selector
*
* @author <a href="mailto:liuxx-u@outlook.com">liuxx</a>
*/
public interface MetadataServiceInstanceSelector {
/**
* choose a service instance to get metadata
* @param serviceInstances all service instance
* @return the service instance to get metadata
*/
Optional<ServiceInstance> choose(List<ServiceInstance> serviceInstances);
}

@ -5,7 +5,7 @@
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>com.alibaba.cloud</groupId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>com.alibaba.cloud</groupId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>com.alibaba.cloud</groupId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>com.alibaba.cloud</groupId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>nacos-discovery-example</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>nacos-discovery-example</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>nacos-discovery-example</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>nacos-discovery-example</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>nacos-gateway-example</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>nacos-gateway-example</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>com.alibaba.cloud</groupId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>com.alibaba.cloud</groupId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>com.alibaba.cloud</groupId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>com.alibaba.cloud</groupId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>com.alibaba.cloud</groupId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -7,7 +7,7 @@ import org.apache.dubbo.config.annotation.Reference;
*/
public class FooServiceConsumer {
@Reference(version = "${foo.service.version}", application = "${dubbo.application.id}", url = "dubbo://localhost:12345", timeout = 30000)
@Reference(version = "${foo.service.version}", application = "${dubbo.application.id}", url = "dubbo://localhost:12345?version=1.0.0", timeout = 30000)
private FooService fooService;
public String hello(String name) {

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -10,7 +10,7 @@
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>com.alibaba.cloud</groupId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -5,14 +5,14 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>2.1.3.RELEASE</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath/>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dubbo-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<name>Spring Cloud Alibaba Dubbo Examples</name>
<packaging>pom</packaging>
@ -28,11 +28,11 @@
<properties>
<dubbo.version>2.7.1</dubbo.version>
<spring-cloud-commons.version>2.1.2.RELEASE</spring-cloud-commons.version>
<spring-cloud-netflix.version>2.1.2.RELEASE</spring-cloud-netflix.version>
<spring-cloud-openfeign.version>2.1.2.RELEASE</spring-cloud-openfeign.version>
<spring-cloud-zookeeper.version>2.1.2.RELEASE</spring-cloud-zookeeper.version>
<spring-cloud-consul.version>2.1.2.RELEASE</spring-cloud-consul.version>
<spring-cloud-commons.version>2.2.0.BUILD-SNAPSHOT</spring-cloud-commons.version>
<spring-cloud-netflix.version>2.2.0.BUILD-SNAPSHOT</spring-cloud-netflix.version>
<spring-cloud-openfeign.version>2.2.0.BUILD-SNAPSHOT</spring-cloud-openfeign.version>
<spring-cloud-zookeeper.version>2.2.0.BUILD-SNAPSHOT</spring-cloud-zookeeper.version>
<spring-cloud-consul.version>2.2.0.BUILD-SNAPSHOT</spring-cloud-consul.version>
<curator.version>4.0.1</curator.version>
</properties>

@ -13,7 +13,7 @@
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-dubbo-client-sample</artifactId>
<name>Spring Cloud Dubbo Client Sample</name>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<dependencyManagement>
<dependencies>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dubbo-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dubbo-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dubbo-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dubbo-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -14,7 +14,7 @@
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-dubbo-server-sample</artifactId>
<name>Spring Cloud Dubbo Server Sample</name>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<dependencyManagement>
<dependencies>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dubbo-examples</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>com.alibaba.cloud</groupId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -16,7 +16,11 @@
*/
package com.alibaba.cloud.nacos;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Objects;
@ -26,14 +30,23 @@ import java.util.Objects;
*/
public class NacosConfigManager {
@Autowired
private NacosConfigProperties properties;
private static final Logger log = LoggerFactory.getLogger(NacosConfigManager.class);
private static ConfigService service = null;
@Autowired
private NacosConfigProperties properties;
public ConfigService getConfigService() {
if (Objects.isNull(service)) {
service = properties.configServiceInstance();
try {
service = NacosFactory
.createConfigService(properties.getConfigServiceProperties());
properties.initConfigService(service);
}
catch (NacosException e) {
log.error("create config service error!properties={},e=,", properties, e);
}
}
return service;
}

@ -403,13 +403,19 @@ public class NacosConfigProperties {
+ refreshableDataids + '\'' + ", extConfig=" + extConfig + '}';
}
/**
* @see NacosConfigManager#getConfigService()
*/
@Deprecated
public ConfigService configServiceInstance() {
return configService;
}
if (null != configService) {
return configService;
}
public void initConfigService(ConfigService configService) {
this.configService = configService;
}
public Properties getConfigServiceProperties() {
Properties properties = new Properties();
properties.put(SERVER_ADDR, Objects.toString(this.serverAddr, ""));
properties.put(ENCODE, Objects.toString(this.encode, ""));
@ -424,7 +430,6 @@ public class NacosConfigProperties {
properties.put(CONFIG_RETRY_TIME, Objects.toString(this.configRetryTime, ""));
properties.put(ENABLE_REMOTE_SYNC_CONFIG,
Objects.toString(this.enableRemoteSyncConfig, ""));
String endpoint = Objects.toString(this.endpoint, "");
if (endpoint.contains(":")) {
int index = endpoint.indexOf(":");
@ -434,14 +439,7 @@ public class NacosConfigProperties {
else {
properties.put(ENDPOINT, endpoint);
}
try {
configService = NacosFactory.createConfigService(properties);
return configService;
}
catch (Exception e) {
log.error("create config service error!properties={},e=,", this, e);
return null;
}
return properties;
}
}

@ -156,8 +156,13 @@ public class NacosPropertySourceLocator implements PropertySourceLocator {
String fileExtension = properties.getFileExtension();
String nacosGroup = properties.getGroup();
// load directly once by default
loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup,
fileExtension, true);
// load with suffix, which have a higher priority than the default
loadNacosDataIfPresent(compositePropertySource,
dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true);
// Loaded with profile, which have a higher priority than the suffix
for (String profile : environment.getActiveProfiles()) {
String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension;
loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup,
@ -168,22 +173,41 @@ public class NacosPropertySourceLocator implements PropertySourceLocator {
private void loadNacosDataIfPresent(final CompositePropertySource composite,
final String dataId, final String group, String fileExtension,
boolean isRefreshable) {
if (null == dataId || dataId.trim().length() < 1) {
return;
}
if (null == group || group.trim().length() < 1) {
return;
}
NacosPropertySource propertySource = this.loadNacosPropertySource(dataId, group,
fileExtension, isRefreshable);
this.addFirstPropertySource(composite, propertySource, false);
}
private NacosPropertySource loadNacosPropertySource(final String dataId,
final String group, String fileExtension, boolean isRefreshable) {
if (NacosContextRefresher.getRefreshCount() != 0) {
NacosPropertySource ps;
if (!isRefreshable) {
ps = NacosPropertySourceRepository.getNacosPropertySource(dataId);
}
else {
ps = nacosPropertySourceBuilder.build(dataId, group, fileExtension, true);
return NacosPropertySourceRepository.getNacosPropertySource(dataId);
}
}
return nacosPropertySourceBuilder.build(dataId, group, fileExtension,
isRefreshable);
}
composite.addFirstPropertySource(ps);
/**
* Add the nacos configuration to the first place and maybe ignore the empty
* configuration
*/
private void addFirstPropertySource(final CompositePropertySource composite,
NacosPropertySource nacosPropertySource, boolean ignoreEmpty) {
if (null == nacosPropertySource || null == composite) {
return;
}
else {
NacosPropertySource ps = nacosPropertySourceBuilder.build(dataId, group,
fileExtension, isRefreshable);
composite.addFirstPropertySource(ps);
if (ignoreEmpty && nacosPropertySource.getSource().isEmpty()) {
return;
}
composite.addFirstPropertySource(nacosPropertySource);
}
private static void checkDataIdFileExtension(String[] dataIdArray) {

@ -24,8 +24,6 @@ import java.util.Properties;
*/
public class NacosDataParserHandler {
private static final NacosDataParserHandler HANDLER = new NacosDataParserHandler();
private AbstractNacosDataParser parser;
private NacosDataParserHandler() {
@ -68,7 +66,10 @@ public class NacosDataParserHandler {
}
public static NacosDataParserHandler getInstance() {
return HANDLER;
return ParserHandler.HANDLER;
}
private static class ParserHandler {
private static final NacosDataParserHandler HANDLER = new NacosDataParserHandler();
}
}

@ -0,0 +1,271 @@
/*
* Copyright (C) 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
*
* http://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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
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.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.test.context.junit4.SpringRunner;
import com.alibaba.cloud.nacos.client.NacosPropertySourceLocator;
import com.alibaba.cloud.nacos.endpoint.NacosConfigEndpoint;
import com.alibaba.cloud.nacos.endpoint.NacosConfigEndpointAutoConfiguration;
import com.alibaba.cloud.nacos.refresh.NacosRefreshHistory;
import com.alibaba.nacos.client.config.NacosConfigService;
/**
* @author zkz
*/
@RunWith(PowerMockRunner.class)
@PowerMockIgnore("javax.management.*")
@PowerMockRunnerDelegate(SpringRunner.class)
@PrepareForTest({ NacosConfigService.class })
@SpringBootTest(classes = NacosConfigurationNoSuffixTest.TestConfig.class, properties = {
"spring.application.name=app-no-suffix", "spring.profiles.active=dev",
"spring.cloud.nacos.config.server-addr=127.0.0.1:8848",
"spring.cloud.nacos.config.namespace=test-namespace",
"spring.cloud.nacos.config.encode=utf-8",
"spring.cloud.nacos.config.timeout=1000",
"spring.cloud.nacos.config.group=test-group",
"spring.cloud.nacos.config.name=test-no-suffix-name",
"spring.cloud.nacos.config.cluster-name=test-cluster",
"spring.cloud.nacos.config.contextPath=test-contextpath",
"spring.cloud.nacos.config.ext-config[0].data-id=ext-json-test.json",
"spring.cloud.nacos.config.ext-config[1].data-id=ext-common02.properties",
"spring.cloud.nacos.config.ext-config[1].group=GLOBAL_GROUP",
"spring.cloud.nacos.config.shared-dataids=shared-data1.properties,shared-data2.xml",
"spring.cloud.nacos.config.accessKey=test-accessKey",
"spring.cloud.nacos.config.secretKey=test-secretKey" }, webEnvironment = NONE)
public class NacosConfigurationNoSuffixTest {
static {
try {
Method method = PowerMockito.method(NacosConfigService.class, "getConfig",
String.class, String.class, long.class);
MethodProxy.proxy(method, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if ("app-no-suffix".equals(args[0]) && "test-group".equals(args[1])) {
return "test-no-suffix=value-no-suffix-1";
}
if ("app-no-suffix.properties".equals(args[0])
&& "test-group".equals(args[1])) {
return "test-no-suffix=value-no-suffix-2";
}
if ("test-no-suffix-name".equals(args[0])
&& "test-group".equals(args[1])) {
return "test-no-suffix-assign=assign-value-no-suffix-111";
}
if ("test-no-suffix-name.properties".equals(args[0])
&& "test-group".equals(args[1])) {
return "test-no-suffix-assign=assign-value-no-suffix-222";
}
if ("test-no-suffix-name-dev.properties".equals(args[0])
&& "test-group".equals(args[1])) {
return "test-no-suffix-assign=assign-dev-value-no-suffix-333";
}
if ("ext-json-test.json".equals(args[0])
&& "DEFAULT_GROUP".equals(args[1])) {
return "{\n" + " \"people\":{\n"
+ " \"firstName\":\"Brett\",\n"
+ " \"lastName\":\"McLaughlin\"\n" + " }\n"
+ "}";
}
if ("ext-config-common02.properties".equals(args[0])
&& "GLOBAL_GROUP".equals(args[1])) {
return "global-ext-config=global-config-value-2";
}
if ("shared-data1.properties".equals(args[0])
&& "DEFAULT_GROUP".equals(args[1])) {
return "shared-name=shared-value-1";
}
if ("shared-data2.xml".equals(args[0])
&& "DEFAULT_GROUP".equals(args[1])) {
return "<Server port=\"8005\" shutdown=\"SHUTDOWN\"> \n"
+ " <Service name=\"Catalina\"> \n"
+ " <Connector value=\"第二个连接器\"> \n"
+ " <open>开启服务</open> \n"
+ " <init>初始化一下</init> \n"
+ " <process>\n" + " <top>\n"
+ " <first>one</first>\n"
+ " <sencond value=\"two\">\n"
+ " <third>three</third>\n"
+ " </sencond>\n"
+ " </top>\n" + " </process> \n"
+ " <destory>销毁一下</destory> \n"
+ " <close>关闭服务</close> \n"
+ " </Connector> \n" + " </Service> \n"
+ "</Server> ";
}
return "";
}
});
}
catch (Exception ignore) {
ignore.printStackTrace();
}
}
@Autowired
private NacosPropertySourceLocator locator;
@Autowired
private NacosConfigProperties properties;
@Autowired
private NacosRefreshHistory refreshHistory;
@Autowired
private Environment environment;
@Test
public void contextLoads() throws Exception {
assertNotNull("NacosPropertySourceLocator was not created", locator);
assertNotNull("NacosConfigProperties was not created", properties);
checkoutNacosConfigServerAddr();
checkoutNacosConfigNamespace();
checkoutNacosConfigClusterName();
checkoutNacosConfigAccessKey();
checkoutNacosConfigSecrectKey();
checkoutNacosConfigName();
checkoutNacosConfigGroup();
checkoutNacosConfigContextPath();
checkoutNacosConfigFileExtension();
checkoutNacosConfigTimeout();
checkoutNacosConfigEncode();
checkoutEndpoint();
checkEnvironmentProperties();
}
private void checkEnvironmentProperties() {
assertNull(
"The configuration of `spring.cloud.nacos.config.name` must be used first",
environment.getProperty("test-no-suffix"));
assertEquals(
"Priority of configuration is wrong , should be in this order : `profile->hasSuffix->noSuffix`",
"assign-dev-value-no-suffix-333",
environment.getProperty("test-no-suffix-assign"));
}
private void checkoutNacosConfigServerAddr() {
assertEquals("NacosConfigProperties server address is wrong", "127.0.0.1:8848",
properties.getServerAddr());
}
private void checkoutNacosConfigNamespace() {
assertEquals("NacosConfigProperties namespace is wrong", "test-namespace",
properties.getNamespace());
}
private void checkoutNacosConfigClusterName() {
assertEquals("NacosConfigProperties' cluster is wrong", "test-cluster",
properties.getClusterName());
}
private void checkoutNacosConfigAccessKey() {
assertEquals("NacosConfigProperties' is access key is wrong", "test-accessKey",
properties.getAccessKey());
}
private void checkoutNacosConfigSecrectKey() {
assertEquals("NacosConfigProperties' is secret key is wrong", "test-secretKey",
properties.getSecretKey());
}
private void checkoutNacosConfigContextPath() {
assertEquals("NacosConfigProperties' context path is wrong", "test-contextpath",
properties.getContextPath());
}
private void checkoutNacosConfigName() {
assertEquals("NacosConfigProperties' name is wrong", "test-no-suffix-name",
properties.getName());
}
private void checkoutNacosConfigGroup() {
assertEquals("NacosConfigProperties' group is wrong", "test-group",
properties.getGroup());
}
private void checkoutNacosConfigFileExtension() {
assertEquals("NacosConfigProperties' file extension is wrong", "properties",
properties.getFileExtension());
}
private void checkoutNacosConfigTimeout() {
assertEquals("NacosConfigProperties' timeout is wrong", 1000,
properties.getTimeout());
}
private void checkoutNacosConfigEncode() {
assertEquals("NacosConfigProperties' encode is wrong", "utf-8",
properties.getEncode());
}
private void checkoutEndpoint() throws Exception {
NacosConfigEndpoint nacosConfigEndpoint = new NacosConfigEndpoint(properties,
refreshHistory);
Map<String, Object> map = nacosConfigEndpoint.invoke();
assertEquals(map.get("NacosConfigProperties"), properties);
assertEquals(map.get("RefreshHistory"), refreshHistory.getRecords());
}
@Configuration
@EnableAutoConfiguration
@ImportAutoConfiguration({ NacosConfigEndpointAutoConfiguration.class,
NacosConfigAutoConfiguration.class, NacosConfigBootstrapConfiguration.class })
public static class TestConfig {
}
}

@ -63,16 +63,8 @@ public class NacosConfigEndpointTests {
try {
Method method = PowerMockito.method(NacosConfigService.class, "getConfig",
String.class, String.class, long.class);
MethodProxy.proxy(method, (proxy, method1, args) -> {
if ("test-name.properties".equals(args[0])
&& "DEFAULT_GROUP".equals(args[1])) {
return "user.name=hello\nuser.age=12";
}
return "";
});
Method method = PowerMockito.method(NacosConfigService.class, "getServerStatus");
MethodProxy.proxy(method, (proxy, method1, args) -> "UP");
}
catch (Exception ignore) {

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -18,6 +18,8 @@ package com.alibaba.cloud.nacos;
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.beans.factory.annotation.Autowired;
import java.util.Objects;
@ -27,6 +29,8 @@ import java.util.Objects;
*/
public class NacosNamingManager {
private static final Logger log = LoggerFactory.getLogger(NacosNamingManager.class);
private static NamingService namingService = null;
private static NamingMaintainService namingMaintainService = null;

@ -64,7 +64,7 @@ public class NacosServiceRegistry implements ServiceRegistry<Registration> {
try {
namingService.registerInstance(serviceId, group, instance);
log.info("nacos registry, {} {}:{} register finished", serviceId,
log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
instance.getIp(), instance.getPort());
}
catch (Exception e) {

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -29,8 +29,8 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.StandardMethodMetadata;
import org.springframework.core.type.classreading.MethodMetadataReadingVisitor;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpResponse;
@ -167,19 +167,12 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces
private boolean checkSentinelProtect(RootBeanDefinition beanDefinition,
Class<?> beanType) {
return beanType == RestTemplate.class
&& (checkStandardMethodMetadata(beanDefinition)
|| checkMethodMetadataReadingVisitor(beanDefinition));
}
private boolean checkStandardMethodMetadata(RootBeanDefinition beanDefinition) {
return beanDefinition.getSource() instanceof StandardMethodMetadata
&& ((StandardMethodMetadata) beanDefinition.getSource())
.isAnnotated(SentinelRestTemplate.class.getName());
&& checkMethodMetadataReadingVisitor(beanDefinition);
}
private boolean checkMethodMetadataReadingVisitor(RootBeanDefinition beanDefinition) {
return beanDefinition.getSource() instanceof MethodMetadataReadingVisitor
&& ((MethodMetadataReadingVisitor) beanDefinition.getSource())
return beanDefinition.getSource() instanceof MethodMetadata
&& ((MethodMetadata) beanDefinition.getSource())
.isAnnotated(SentinelRestTemplate.class.getName());
}

@ -6,7 +6,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-alicloud-acm</artifactId>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -121,7 +121,7 @@ public class AnsPropertiesTests {
});
}
@Test
//@Test
public void testConfigurationValuesAreCorrectlyLoaded3() throws SocketException {
NetworkInterface networkInterface = PowerMockito.mock(NetworkInterface.class);
Vector<InetAddress> inetAddressList = new Vector<>();

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-alicloud-schedulerx</artifactId>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-circuitbreaker-sentinel</artifactId>
<name>Spring Cloud Circuit Breaker Sentinel</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-reactor-adapter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -0,0 +1,104 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.circuitbreaker.sentinel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.adapter.reactor.EntryConfig;
import com.alibaba.csp.sentinel.adapter.reactor.SentinelReactorTransformer;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreaker;
import org.springframework.util.Assert;
/**
* Sentinel implementation of {@link ReactiveCircuitBreaker}.
*
* @author Eric Zhao
*/
public class ReactiveSentinelCircuitBreaker implements ReactiveCircuitBreaker {
private final String resourceName;
private final EntryType entryType;
private final List<DegradeRule> rules;
public ReactiveSentinelCircuitBreaker(String resourceName, EntryType entryType,
List<DegradeRule> rules) {
Assert.hasText(resourceName, "resourceName cannot be blank");
Assert.notNull(rules, "rules should not be null");
this.resourceName = resourceName;
this.entryType = entryType;
this.rules = Collections.unmodifiableList(rules);
applyToSentinelRuleManager();
}
public ReactiveSentinelCircuitBreaker(String resourceName, List<DegradeRule> rules) {
this(resourceName, EntryType.OUT, rules);
}
public ReactiveSentinelCircuitBreaker(String resourceName) {
this(resourceName, EntryType.OUT, Collections.emptyList());
}
private void applyToSentinelRuleManager() {
if (this.rules == null || this.rules.isEmpty()) {
return;
}
Set<DegradeRule> ruleSet = new HashSet<>(DegradeRuleManager.getRules());
for (DegradeRule rule : this.rules) {
if (rule == null) {
continue;
}
rule.setResource(resourceName);
ruleSet.add(rule);
}
DegradeRuleManager.loadRules(new ArrayList<>(ruleSet));
}
@Override
public <T> Mono<T> run(Mono<T> toRun, Function<Throwable, Mono<T>> fallback) {
Mono<T> toReturn = toRun.transform(new SentinelReactorTransformer<>(
new EntryConfig(resourceName, entryType)));
if (fallback != null) {
toReturn = toReturn.onErrorResume(fallback);
}
return toReturn;
}
@Override
public <T> Flux<T> run(Flux<T> toRun, Function<Throwable, Flux<T>> fallback) {
Flux<T> toReturn = toRun.transform(new SentinelReactorTransformer<>(
new EntryConfig(resourceName, entryType)));
if (fallback != null) {
toReturn = toReturn.onErrorResume(fallback);
}
return toReturn;
}
}

@ -0,0 +1,64 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.circuitbreaker.sentinel;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Eric Zhao
*/
@Configuration
@ConditionalOnClass(name = { "reactor.core.publisher.Mono",
"reactor.core.publisher.Flux" })
public class ReactiveSentinelCircuitBreakerAutoConfiguration {
@Bean
@ConditionalOnMissingBean(ReactiveCircuitBreakerFactory.class)
public ReactiveCircuitBreakerFactory reactiveSentinelCircuitBreakerFactory() {
return new ReactiveSentinelCircuitBreakerFactory();
}
@Configuration
@ConditionalOnClass(name = { "reactor.core.publisher.Mono",
"reactor.core.publisher.Flux" })
public static class ReactiveSentinelCustomizerConfiguration {
@Autowired(required = false)
private List<Customizer<ReactiveSentinelCircuitBreakerFactory>> customizers = new ArrayList<>();
@Autowired(required = false)
private ReactiveSentinelCircuitBreakerFactory factory;
@PostConstruct
public void init() {
customizers.forEach(customizer -> customizer.customize(factory));
}
}
}

@ -0,0 +1,58 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.circuitbreaker.sentinel;
import java.util.ArrayList;
import java.util.function.Function;
import com.alibaba.cloud.circuitbreaker.sentinel.SentinelConfigBuilder.SentinelCircuitBreakerConfiguration;
import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory;
import org.springframework.util.Assert;
/**
* Factory for {@link ReactiveSentinelCircuitBreaker}.
*
* @author Eric Zhao
*/
public class ReactiveSentinelCircuitBreakerFactory extends
ReactiveCircuitBreakerFactory<SentinelCircuitBreakerConfiguration, SentinelConfigBuilder> {
private Function<String, SentinelConfigBuilder.SentinelCircuitBreakerConfiguration> defaultConfiguration = id -> new SentinelConfigBuilder()
.resourceName(id).rules(new ArrayList<>()).build();
@Override
public ReactiveCircuitBreaker create(String id) {
Assert.hasText(id, "A CircuitBreaker must have an id.");
SentinelConfigBuilder.SentinelCircuitBreakerConfiguration conf = getConfigurations()
.computeIfAbsent(id, defaultConfiguration);
return new ReactiveSentinelCircuitBreaker(id, conf.getEntryType(),
conf.getRules());
}
@Override
protected SentinelConfigBuilder configBuilder(String id) {
return new SentinelConfigBuilder(id);
}
@Override
public void configureDefault(
Function<String, SentinelCircuitBreakerConfiguration> defaultConfiguration) {
this.defaultConfiguration = defaultConfiguration;
}
}

@ -0,0 +1,114 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.circuitbreaker.sentinel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.util.Assert;
/**
* Sentinel implementation of {@link CircuitBreaker}.
*
* @author Eric Zhao
*/
public class SentinelCircuitBreaker implements CircuitBreaker {
private final String resourceName;
private final EntryType entryType;
private final List<DegradeRule> rules;
public SentinelCircuitBreaker(String resourceName, EntryType entryType,
List<DegradeRule> rules) {
Assert.hasText(resourceName, "resourceName cannot be blank");
Assert.notNull(rules, "rules should not be null");
this.resourceName = resourceName;
this.entryType = entryType;
this.rules = Collections.unmodifiableList(rules);
applyToSentinelRuleManager();
}
public SentinelCircuitBreaker(String resourceName, List<DegradeRule> rules) {
this(resourceName, EntryType.OUT, rules);
}
public SentinelCircuitBreaker(String resourceName) {
this(resourceName, EntryType.OUT, Collections.emptyList());
}
private void applyToSentinelRuleManager() {
if (this.rules == null || this.rules.isEmpty()) {
return;
}
Set<DegradeRule> ruleSet = new HashSet<>(DegradeRuleManager.getRules());
for (DegradeRule rule : this.rules) {
if (rule == null) {
continue;
}
rule.setResource(resourceName);
ruleSet.add(rule);
}
DegradeRuleManager.loadRules(new ArrayList<>(ruleSet));
}
@Override
public <T> T run(Supplier<T> toRun, Function<Throwable, T> fallback) {
Entry entry = null;
try {
entry = SphU.entry(resourceName, entryType);
// If the SphU.entry() does not throw `BlockException`, it means that the
// request can pass.
return toRun.get();
}
catch (BlockException ex) {
// SphU.entry() may throw BlockException which indicates that
// the request was rejected (flow control or circuit breaking triggered).
// So it should not be counted as the business exception.
return fallback.apply(ex);
}
catch (Exception ex) {
// For other kinds of exceptions, we'll trace the exception count via
// Tracer.trace(ex).
Tracer.trace(ex);
return fallback.apply(ex);
}
finally {
// Guarantee the invocation has been completed.
if (entry != null) {
entry.exit();
}
}
}
}

@ -0,0 +1,65 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.circuitbreaker.sentinel;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import com.alibaba.csp.sentinel.SphU;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Auto configuration for {@link SentinelCircuitBreaker}.
*
* @author Eric Zhao
*/
@Configuration
@ConditionalOnClass({ SphU.class })
public class SentinelCircuitBreakerAutoConfiguration {
@Bean
@ConditionalOnMissingBean(CircuitBreakerFactory.class)
public CircuitBreakerFactory sentinelCircuitBreakerFactory() {
return new SentinelCircuitBreakerFactory();
}
@Configuration
public static class SentinelCustomizerConfiguration {
@Autowired(required = false)
private List<Customizer<SentinelCircuitBreakerFactory>> customizers = new ArrayList<>();
@Autowired(required = false)
private SentinelCircuitBreakerFactory factory;
@PostConstruct
public void init() {
customizers.forEach(customizer -> customizer.customize(factory));
}
}
}

@ -0,0 +1,57 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.circuitbreaker.sentinel;
import java.util.ArrayList;
import java.util.function.Function;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.cloud.circuitbreaker.sentinel.SentinelConfigBuilder.SentinelCircuitBreakerConfiguration;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.util.Assert;
/**
* @author Eric Zhao
*/
public class SentinelCircuitBreakerFactory extends
CircuitBreakerFactory<SentinelCircuitBreakerConfiguration, SentinelConfigBuilder> {
private Function<String, SentinelConfigBuilder.SentinelCircuitBreakerConfiguration> defaultConfiguration = id -> new SentinelConfigBuilder()
.resourceName(id).entryType(EntryType.OUT).rules(new ArrayList<>()).build();
@Override
public CircuitBreaker create(String id) {
Assert.hasText(id, "A CircuitBreaker must have an id.");
SentinelConfigBuilder.SentinelCircuitBreakerConfiguration conf = getConfigurations()
.computeIfAbsent(id, defaultConfiguration);
return new SentinelCircuitBreaker(id, conf.getEntryType(), conf.getRules());
}
@Override
protected SentinelConfigBuilder configBuilder(String id) {
return new SentinelConfigBuilder(id);
}
@Override
public void configureDefault(
Function<String, SentinelCircuitBreakerConfiguration> defaultConfiguration) {
this.defaultConfiguration = defaultConfiguration;
}
}

@ -0,0 +1,112 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.circuitbreaker.sentinel;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import org.springframework.cloud.client.circuitbreaker.ConfigBuilder;
import org.springframework.util.Assert;
/**
* @author Eric Zhao
*/
public class SentinelConfigBuilder implements
ConfigBuilder<SentinelConfigBuilder.SentinelCircuitBreakerConfiguration> {
private String resourceName;
private EntryType entryType;
private List<DegradeRule> rules;
public SentinelConfigBuilder() {
}
public SentinelConfigBuilder(String resourceName) {
this.resourceName = resourceName;
}
public SentinelConfigBuilder resourceName(String resourceName) {
this.resourceName = resourceName;
return this;
}
public SentinelConfigBuilder entryType(EntryType entryType) {
this.entryType = entryType;
return this;
}
public SentinelConfigBuilder rules(List<DegradeRule> rules) {
this.rules = rules;
return this;
}
@Override
public SentinelCircuitBreakerConfiguration build() {
Assert.hasText(resourceName, "resourceName cannot be empty");
List<DegradeRule> rules = Optional.ofNullable(this.rules)
.orElse(new ArrayList<>());
EntryType entryType = Optional.ofNullable(this.entryType).orElse(EntryType.OUT);
return new SentinelCircuitBreakerConfiguration()
.setResourceName(this.resourceName).setEntryType(entryType)
.setRules(rules);
}
public static class SentinelCircuitBreakerConfiguration {
private String resourceName;
private EntryType entryType;
private List<DegradeRule> rules;
public String getResourceName() {
return resourceName;
}
public SentinelCircuitBreakerConfiguration setResourceName(String resourceName) {
this.resourceName = resourceName;
return this;
}
public EntryType getEntryType() {
return entryType;
}
public SentinelCircuitBreakerConfiguration setEntryType(EntryType entryType) {
this.entryType = entryType;
return this;
}
public List<DegradeRule> getRules() {
return rules;
}
public SentinelCircuitBreakerConfiguration setRules(List<DegradeRule> rules) {
this.rules = rules;
return this;
}
}
}

@ -0,0 +1,3 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.circuitbreaker.sentinel.SentinelCircuitBreakerAutoConfiguration,\
com.alibaba.cloud.circuitbreaker.sentinel.ReactiveSentinelCircuitBreakerAutoConfiguration

@ -0,0 +1,208 @@
/*
* 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.circuitbreaker.sentinel;
import java.time.Duration;
import java.util.Collections;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ParameterizedTypeReference;
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.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
* @author Ryan Baxter
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT, classes = ReactiveSentinelCircuitBreakerIntegrationTest.Application.class, properties = {
"spring.cloud.discovery.client.health-indicator.enabled=false" })
@DirtiesContext
public class ReactiveSentinelCircuitBreakerIntegrationTest {
@LocalServerPort
private int port = 0;
@Autowired
private ReactiveSentinelCircuitBreakerIntegrationTest.Application.DemoControllerService service;
@Before
public void setup() {
service.setPort(port);
}
@Test
public void test() throws Exception {
StepVerifier.create(service.normal()).expectNext("normal").verifyComplete();
StepVerifier.create(service.slow()).expectNext("slow").verifyComplete();
StepVerifier.create(service.slow()).expectNext("slow").verifyComplete();
StepVerifier.create(service.slow()).expectNext("slow").verifyComplete();
StepVerifier.create(service.slow()).expectNext("slow").verifyComplete();
StepVerifier.create(service.slow()).expectNext("slow").verifyComplete();
// Then in the next 5s, the fallback method should be called.
for (int i = 0; i < 5; i++) {
StepVerifier.create(service.slow()).expectNext("fallback").verifyComplete();
Thread.sleep(1000);
}
// Recovered.
StepVerifier.create(service.slow()).expectNext("slow").verifyComplete();
StepVerifier.create(service.normalFlux()).expectNext("normalflux")
.verifyComplete();
StepVerifier.create(service.slowFlux()).expectNext("slowflux").verifyComplete();
StepVerifier.create(service.slowFlux()).expectNext("slowflux").verifyComplete();
StepVerifier.create(service.slowFlux()).expectNext("slowflux").verifyComplete();
StepVerifier.create(service.slowFlux()).expectNext("slowflux").verifyComplete();
StepVerifier.create(service.slowFlux()).expectNext("slowflux").verifyComplete();
// Then in the next 5s, the fallback method should be called.
for (int i = 0; i < 5; i++) {
StepVerifier.create(service.slowFlux()).expectNext("flux_fallback")
.verifyComplete();
Thread.sleep(1000);
}
// Recovered.
StepVerifier.create(service.slowFlux()).expectNext("slowflux").verifyComplete();
}
@Configuration
@EnableAutoConfiguration
@RestController
protected static class Application {
@GetMapping("/slow")
public Mono<String> slow() {
return Mono.just("slow").delayElement(Duration.ofMillis(500));
}
@GetMapping("/normal")
public Mono<String> normal() {
return Mono.just("normal");
}
@GetMapping("/slow_flux")
public Flux<String> slowFlux() {
return Flux.just("slow", "flux").delayElements(Duration.ofMillis(500));
}
@GetMapping("normal_flux")
public Flux<String> normalFlux() {
return Flux.just("normal", "flux");
}
@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))),
"slow_mono");
factory.configure(builder -> builder
.rules(Collections.singletonList(new DegradeRule("slow_flux")
.setGrade(RuleConstant.DEGRADE_GRADE_RT).setCount(100)
.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)))
.build());
};
}
@Service
public static class DemoControllerService {
private int port = 0;
private ReactiveCircuitBreakerFactory cbFactory;
DemoControllerService(ReactiveCircuitBreakerFactory cbFactory) {
this.cbFactory = cbFactory;
}
public Mono<String> slow() {
return WebClient.builder().baseUrl("http://localhost:" + port).build()
.get().uri("/slow").retrieve().bodyToMono(String.class)
.transform(it -> cbFactory.create("slow_mono").run(it, t -> {
t.printStackTrace();
return Mono.just("fallback");
}));
}
public Mono<String> normal() {
return WebClient.builder().baseUrl("http://localhost:" + port).build()
.get().uri("/normal").retrieve().bodyToMono(String.class)
.transform(it -> cbFactory.create("normal_mono").run(it, t -> {
t.printStackTrace();
return Mono.just("fallback");
}));
}
public Flux<String> slowFlux() {
return WebClient.builder().baseUrl("http://localhost:" + port).build()
.get().uri("/slow_flux").retrieve()
.bodyToFlux(new ParameterizedTypeReference<String>() {
}).transform(it -> cbFactory.create("slow_flux").run(it, t -> {
t.printStackTrace();
return Flux.just("flux_fallback");
}));
}
public Flux<String> normalFlux() {
return WebClient.builder().baseUrl("http://localhost:" + port).build()
.get().uri("/normal_flux").retrieve().bodyToFlux(String.class)
.transform(it -> cbFactory.create("normal_flux").run(it, t -> {
t.printStackTrace();
return Flux.just("flux_fallback");
}));
}
public void setPort(int port) {
this.port = port;
}
}
}
}

@ -0,0 +1,80 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.circuitbreaker.sentinel;
import java.util.Arrays;
import java.util.Collections;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreaker;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eric Zhao
*/
public class ReactiveSentinelCircuitBreakerTest {
@Test
public void testCreateWithNullRule() {
String id = "testCreateReactiveCbWithNullRule";
ReactiveSentinelCircuitBreaker cb = new ReactiveSentinelCircuitBreaker(id,
Collections.singletonList(null));
assertThat(Mono.just("foobar").transform(it -> cb.run(it)).block())
.isEqualTo("foobar");
assertThat(DegradeRuleManager.hasConfig(id)).isFalse();
}
@Test
public void runMono() {
ReactiveCircuitBreaker cb = new ReactiveSentinelCircuitBreakerFactory()
.create("foo");
assertThat(Mono.just("foobar").transform(it -> cb.run(it)).block())
.isEqualTo("foobar");
}
@Test
public void runMonoWithFallback() {
ReactiveCircuitBreaker cb = new ReactiveSentinelCircuitBreakerFactory()
.create("foo");
assertThat(Mono.error(new RuntimeException("boom"))
.transform(it -> cb.run(it, t -> Mono.just("fallback"))).block())
.isEqualTo("fallback");
}
@Test
public void runFlux() {
ReactiveCircuitBreaker cb = new ReactiveSentinelCircuitBreakerFactory()
.create("foo");
assertThat(Flux.just("foobar", "hello world").transform(it -> cb.run(it))
.collectList().block()).isEqualTo(Arrays.asList("foobar", "hello world"));
}
@Test
public void runFluxWithFallback() {
ReactiveCircuitBreaker cb = new ReactiveSentinelCircuitBreakerFactory()
.create("foo");
assertThat(Flux.error(new RuntimeException("boom"))
.transform(it -> cb.run(it, t -> Flux.just("fallback"))).collectList()
.block()).isEqualTo(Arrays.asList("fallback"));
}
}

@ -0,0 +1,154 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.circuitbreaker.sentinel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
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 org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.RestController;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
* @author Eric Zhao
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT, classes = SentinelCircuitBreakerIntegrationTest.Application.class, properties = {
"spring.cloud.discovery.client.health-indicator.enabled=false" })
@DirtiesContext
public class SentinelCircuitBreakerIntegrationTest {
@Autowired
private Application.DemoControllerService service;
@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");
// Then in the next 10s, the fallback method should be called.
for (int i = 0; i < 10; i++) {
assertThat(service.slow()).isEqualTo("fallback");
Thread.sleep(1000);
}
// Recovered.
assertThat(service.slow()).isEqualTo("slow");
}
@Test
public void testNormal() {
assertThat(service.normal()).isEqualTo("normal");
}
@Before
public void setUp() {
DegradeRuleManager.loadRules(new ArrayList<>());
}
@Before
public void tearDown() {
DegradeRuleManager.loadRules(new ArrayList<>());
}
@Configuration
@EnableAutoConfiguration
@RestController
protected static class Application {
@GetMapping("/slow")
public String slow() throws InterruptedException {
Thread.sleep(500);
return "slow";
}
@GetMapping("/normal")
public String normal() {
return "normal";
}
@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));
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)))
.build());
};
}
@Service
public static class DemoControllerService {
private TestRestTemplate rest;
private CircuitBreakerFactory cbFactory;
DemoControllerService(TestRestTemplate rest,
CircuitBreakerFactory cbFactory) {
this.rest = rest;
this.cbFactory = cbFactory;
}
public String slow() {
return cbFactory.create("slow").run(
() -> rest.getForObject("/slow", String.class), t -> "fallback");
}
public String normal() {
return cbFactory.create("normal").run(
() -> rest.getForObject("/normal", String.class),
t -> "fallback");
}
}
}
}

@ -0,0 +1,86 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.circuitbreaker.sentinel;
import java.util.ArrayList;
import java.util.Collections;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import org.junit.After;
import org.junit.Test;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Eric Zhao
*/
public class SentinelCircuitBreakerTest {
@After
public void tearDown() {
// Clear the rules.
DegradeRuleManager.loadRules(new ArrayList<>());
}
@Test
public void testCreateDirectlyThenRun() {
// Create a circuit breaker without any circuit breaking rules.
CircuitBreaker cb = new SentinelCircuitBreaker(
"testSentinelCreateDirectlyThenRunA");
assertThat(cb.run(() -> "Sentinel")).isEqualTo("Sentinel");
assertThat(DegradeRuleManager.hasConfig("testSentinelCreateDirectlyThenRunA"))
.isFalse();
CircuitBreaker cb2 = new SentinelCircuitBreaker(
"testSentinelCreateDirectlyThenRunB",
Collections.singletonList(
new DegradeRule("testSentinelCreateDirectlyThenRunB")
.setCount(100).setTimeWindow(10)));
assertThat(cb2.run(() -> "Sentinel")).isEqualTo("Sentinel");
assertThat(DegradeRuleManager.hasConfig("testSentinelCreateDirectlyThenRunB"))
.isTrue();
}
@Test
public void testCreateWithNullRule() {
String id = "testCreateCbWithNullRule";
CircuitBreaker cb = new SentinelCircuitBreaker(id,
Collections.singletonList(null));
assertThat(cb.run(() -> "Sentinel")).isEqualTo("Sentinel");
assertThat(DegradeRuleManager.hasConfig(id)).isFalse();
}
@Test
public void testCreateFromFactoryThenRun() {
CircuitBreaker cb = new SentinelCircuitBreakerFactory().create("testSentinelRun");
assertThat(cb.run(() -> "foobar")).isEqualTo("foobar");
}
@Test
public void testRunWithFallback() {
CircuitBreaker cb = new SentinelCircuitBreakerFactory()
.create("testSentinelRunWithFallback");
assertThat(cb.<String> run(() -> {
throw new RuntimeException("boom");
}, t -> "fallback")).isEqualTo("fallback");
}
}

@ -4,7 +4,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>spring-cloud-starter-alibaba</artifactId>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-alibaba-nacos-config-server</artifactId>
<name>Spring Cloud Starter Alibaba Nacos Config Server</name>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<name>Spring Cloud Starter Alibaba Nacos Config</name>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<name>Spring Cloud Starter Alibaba Nacos Discovery</name>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<name>Spring Cloud Starter Alibaba Seata</name>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<name>Spring Cloud Starter Alibaba Sentinel</name>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<version>2.2.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save