sync & commit in greenwich

pull/1039/head
fangjian0423 5 years ago
parent 12b9091583
commit 980bc3fd7a

@ -76,6 +76,7 @@
<spring-cloud-openfeign.version>2.1.3.RELEASE</spring-cloud-openfeign.version>
<spring-cloud-bus.version>2.1.3.RELEASE</spring-cloud-bus.version>
<spring-cloud-gateway.version>2.1.3.RELEASE</spring-cloud-gateway.version>
<spring-cloud-consul.version>2.1.3.RELEASE</spring-cloud-consul.version>
<junit.version>4.12</junit.version>
<javax-servlet-api>3.0</javax-servlet-api>
@ -118,6 +119,7 @@
<module>spring-cloud-alicloud-schedulerx</module>
<module>spring-cloud-alicloud-sms</module>
<module>spring-cloud-alibaba-coverage</module>
<module>spring-cloud-alibaba-sidecar</module>
</modules>
<dependencyManagement>
@ -187,6 +189,14 @@
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul-dependencies</artifactId>
<version>${spring-cloud-consul.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
@ -218,6 +228,7 @@
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>${rocketmq.starter.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

@ -20,8 +20,8 @@
<properties>
<sentinel.version>1.6.3</sentinel.version>
<oss.version>3.1.0</oss.version>
<seata.version>0.7.1</seata.version>
<nacos.client.version>1.1.1</nacos.client.version>
<seata.version>0.9.0</seata.version>
<nacos.client.version>1.1.4</nacos.client.version>
<nacos.config.version>0.8.0</nacos.config.version>
<acm.version>1.0.9</acm.version>
<ans.version>1.0.1</ans.version>
@ -173,11 +173,6 @@
<artifactId>sentinel-apache-dubbo-adapter</artifactId>
<version>${sentinel.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>sentinel-dubbo-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-cluster-server-default</artifactId>
@ -290,6 +285,11 @@
<artifactId>spring-cloud-alibaba-dubbo</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sidecar</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Own dependencies - Starters -->
<dependency>
@ -356,6 +356,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sidecar</artifactId>
<version>${project.version}</version>
</dependency>
<!-- SMS -->
<dependency>
<groupId>com.alibaba.cloud</groupId>

@ -0,0 +1,194 @@
== Spring Cloud Alibaba Sidecar
`Spring Cloud Alibaba Sidecar` 是一个用来快速**完美整合** Spring Cloud
与 *异构微服务* 的框架,灵感来自
https://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-sidecar[Spring
Cloud Netflix Sidecar] 。目前支持的服务发现组件:
* Nacos
* Consul
=== 术语
==== 异构微服务
非Spring Cloud应用统称异构微服务。比如你的遗留项目或者非JVM应用。
==== ``完美整合''的三层含义
* 享受服务发现的优势
* 有负载均衡
* 有断路器
=== Why or Why not?
==== 为什么要编写Alibaba Sidecar
原因有两点:
* Spring Cloud子项目 `Spring Cloud Netflix Sidecar`
是可以快速整合异构微服务的。然而Sidecar只支持使用Eureka作为服务发现*如果使用其他服务发现组件就抓瞎了*。
* *Sidecar是基于Zuul 1.x的*Spring
Cloud官方明确声明未来将会逐步淘汰Zuul。今年早些时候我有给Spring
Cloud官方提出需求希望官方实现一个基于Spring Cloud
Gateway的新一代Sidecar然而官方表示并没有该计划。详见https://github.com/spring-cloud/spring-cloud-gateway/issues/735
既然没有,索性自己写了。
==== 为什么不用Service Mesh
* 目前Mesh主要使用场景在Kubernetes领域Istio、Linkerd
2等大多将Kubernetes作为First
Class支持虽然Istio也可部署在非Kubernetes环境而目前业界Spring
Cloud应用未必有试试Mesh的环境
* 使用Alibaba
Sidecar一个小组件就能解决问题了核心代码不超过200行引入整套Mesh方案颇有点屠龙刀杀黄鳝的意思。
=== 原理
* Alibaba
Sidecar根据配置的异构微服务的IP、端口等信息*将异构微服务的IP/端口注册到服务发现组件上*
* Alibaba Sidecar实现了 *健康检查* Alibaba
Sidecar会定时检测异构微服务是否健康。如果发现异构微服务不健康Alibaba
Sidecar会自动将代表异构微服务的Alibaba
Sidecar实例下线如果异构微服务恢复正常则会自动上线。最长延迟是30秒详见
`Alibaba SidecarChecker#check` 。
=== 要求
* 【必须】你的异构微服务需使用HTTP通信。这一点严格来说不算要求因为Spring
Cloud本身就是基于HTTP的
* 【可选】如果微服务配置了 `sidecar.health-check-url`
,则表示开启健康检查,此时,你的异构微服务需实现健康检查(可以是空实现,只要暴露一个端点,返回类似
`{"status": "UP"}` 的字符串即可)。
=== 使用示例
* 如使用Nacos作为服务发现组件详见`spring-cloud-alibaba-examples/spring-cloud-alibaba-sidecar-examples/spring-cloud-alibaba-sidecar-nacos-example`
* 如使用Consul作为服务发现组件详见`spring-cloud-alibaba-examples/spring-cloud-alibaba-sidecar-examples/spring-cloud-alibaba-sidecar-nacos-example`
==== 示例代码以Nacos服务发现为例
* 加依赖:
+
[source,xml]
----
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sidecar</artifactId>
</dependency>
----
* 写配置:
+
[source,yaml]
----
server:
port: 8070
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
discovery:
locator:
enabled: true
application:
name: node-service
sidecar:
# 异构微服务的IP
ip: 127.0.0.1
# 异构微服务的端口
port: 8060
# 异构微服务的健康检查URL
health-check-url: http://localhost:8060/health.json
management:
endpoint:
health:
show-details: always
----
+
配置比较简单就是把Alibaba Sidecar注册到Nacos上然后添加了几行Alibaba
Sidecar的配置。
==== 异构微服务
我准备了一个NodeJS编写的简单微服务。
[source,javascript]
----
var http = require('http');
var url = require("url");
var path = require('path');
// 创建server
var server = http.createServer(function(req, res) {
// 获得请求的路径
var pathname = url.parse(req.url).pathname;
res.writeHead(200, { 'Content-Type' : 'application/json; charset=utf-8' });
// 访问http://localhost:8060/,将会返回{"index":"欢迎来到首页"}
if (pathname === '/') {
res.end(JSON.stringify({ "index" : "欢迎来到首页" }));
}
// 访问http://localhost:8060/health将会返回{"status":"UP"}
else if (pathname === '/health.json') {
res.end(JSON.stringify({ "status" : "UP" }));
}
// 其他情况返回404
else {
res.end("404");
}
});
// 创建监听,并打印日志
server.listen(8060, function() {
console.log('listening on localhost:8060');
});
----
==== 测试
===== 测试1Spring Cloud微服务完美调用异构微服务
为你的Spring Cloud微服务整合Ribbon然后构建 `http://node-service/**`
,就可以请求到异构微服务的 `/**` 了。
示例:
Ribbon请求 `http://node-service/` 会请求到 `http://localhost:8060/`
,以此类推。
至于断路器正常为你的Spring
Cloud微服务整合Sentinel或者Hystirx、Resilience4J即可 。
===== 测试2异构微服务完美调用Spring Cloud微服务
由于Alibaba Sidecar基于Spring Cloud Gateway而网关自带转发能力。
示例:
如果你有一个Spring Cloud微服务叫做 `spring-cloud-microservice`
那么NodeJS应用只需构建
`http://localhost:8070/spring-cloud-microservice/**` Alibaba
Sidecar就会把请求转发到 `spring-cloud-microservice` 的 `/**` 。
而Spring Cloud Gateway是整合了Ribbon的所以实现了负载均衡Spring Cloud
Gateway还可以整合Sentinel或者Hystirx、Resilience4J所以也带有了断路器。
=== Alibaba Sidecar优缺点分析
Alibaba
Sidecar的设计和Sidecar基本一致优缺点和Sidecar的优缺点也是一样的。
优点:
* 接入简单几行代码就可以将异构微服务整合到Spring Cloud生态
* 不侵入原代码
缺点:
* 每接入一个异构微服务实例都需要额外部署一个Alibaba
Sidecar实例增加了部署成本虽然这个成本在Kubernetes环境中几乎可以忽略不计只需将Alibaba
Sidecar实例和异构微服务作为一个Pod部署即可
* 异构微服务调用Spring Cloud微服务时本质是把Alibaba
Sidecar当网关在使用经过了一层转发性能有一定下降。

@ -31,4 +31,4 @@ include::schedulerx.adoc[]
include::sms.adoc[]
include::sidecar.adoc[]

@ -13,9 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.dubbo.autoconfigure;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import com.alibaba.cloud.dubbo.annotation.DubboTransported;
import com.alibaba.cloud.dubbo.client.loadbalancer.DubboMetadataInitializerInterceptor;
import com.alibaba.cloud.dubbo.client.loadbalancer.DubboTransporterInterceptor;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.metadata.resolver.DubboTransportedAttributesResolver;
import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory;
import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.SmartInitializingSingleton;
@ -25,30 +38,25 @@ 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;
import com.alibaba.cloud.dubbo.annotation.DubboTransported;
import com.alibaba.cloud.dubbo.client.loadbalancer.DubboMetadataInitializerInterceptor;
import com.alibaba.cloud.dubbo.client.loadbalancer.DubboTransporterInterceptor;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.metadata.resolver.DubboTransportedAttributesResolver;
import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory;
import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory;
/**
* Dubbo Auto-{@link Configuration} for {@link LoadBalanced @LoadBalanced}
* {@link RestTemplate}
* {@link RestTemplate}.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@ -56,8 +64,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,19 +97,21 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration
@Autowired(required = false)
private Map<String, RestTemplate> restTemplates = Collections.emptyMap();
@Nullable
private ApplicationContext applicationContext;
private ClassLoader classLoader;
/**
* The {@link ClientHttpRequestInterceptor} bean that may be
* {@link LoadBalancerInterceptor} or {@link RetryLoadBalancerInterceptor}
* {@link LoadBalancerInterceptor} or {@link RetryLoadBalancerInterceptor}.
*/
private ClientHttpRequestInterceptor loadBalancerInterceptorBean;
@Override
public void afterSingletonsInstantiated() {
loadBalancerInterceptorBean = retryLoadBalancerInterceptor != null
? retryLoadBalancerInterceptor
: loadBalancerInterceptor;
? retryLoadBalancerInterceptor : loadBalancerInterceptor;
}
/**
@ -109,10 +119,13 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration
* {@link LoadBalanced @LoadBalanced} and {@link LoadBalanced @LoadBalanced} when
* Spring Boot application started (after the callback of
* {@link SmartInitializingSingleton} beans or
* {@link RestTemplateCustomizer#customize(RestTemplate) customization})
* {@link RestTemplateCustomizer#customize(RestTemplate) customization}).
* @param event spring event
*/
@EventListener(ApplicationStartedEvent.class)
public void adaptRestTemplates() {
@EventListener(ContextRefreshedEvent.class)
public void adaptRestTemplates(ContextRefreshedEvent event) {
if (event.getApplicationContext() == this.applicationContext) {
DubboTransportedAttributesResolver attributesResolver = new DubboTransportedAttributesResolver(
environment);
@ -126,11 +139,11 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration
}
}
}
}
/**
* Gets the annotation attributes {@link RestTemplate} bean being annotated
* {@link DubboTransported @DubboTransported}
*
* {@link DubboTransported @DubboTransported}.
* @param beanName the bean name of {@link LoadBalanced @LoadBalanced}
* {@link RestTemplate}
* @param attributesResolver {@link DubboTransportedAttributesResolver}
@ -144,10 +157,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);
}
@ -155,7 +168,6 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration
/**
* Adapt the instance of {@link DubboTransporterInterceptor} to the
* {@link LoadBalancerInterceptor} Bean.
*
* @param restTemplate {@link LoadBalanced @LoadBalanced} {@link RestTemplate} Bean
* @param dubboTranslatedAttributes the annotation dubboTranslatedAttributes
* {@link RestTemplate} bean being annotated
@ -188,4 +200,9 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration
this.classLoader = classLoader;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}

@ -16,11 +16,25 @@
package com.alibaba.cloud.dubbo.autoconfigure;
import java.util.Collection;
import java.util.Optional;
import java.util.function.Supplier;
import com.alibaba.cloud.dubbo.metadata.DubboProtocolConfigSupplier;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.metadata.repository.MetadataServiceInstanceSelector;
import com.alibaba.cloud.dubbo.metadata.resolver.DubboServiceBeanMetadataResolver;
import com.alibaba.cloud.dubbo.metadata.resolver.MetadataResolver;
import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceExporter;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy;
import com.alibaba.cloud.dubbo.service.IntrospectiveDubboMetadataService;
import com.alibaba.cloud.dubbo.util.JSONUtils;
import feign.Contract;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.spring.ServiceBean;
import org.apache.dubbo.config.spring.context.event.ServiceBeanExportedEvent;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -30,18 +44,7 @@ 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 com.alibaba.cloud.dubbo.metadata.DubboProtocolConfigSupplier;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.metadata.resolver.DubboServiceBeanMetadataResolver;
import com.alibaba.cloud.dubbo.metadata.resolver.MetadataResolver;
import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceExporter;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy;
import com.alibaba.cloud.dubbo.service.IntrospectiveDubboMetadataService;
import com.alibaba.cloud.dubbo.util.JSONUtils;
import feign.Contract;
import org.springframework.util.CollectionUtils;
/**
* Spring Boot Auto-Configuration class for Dubbo Metadata
@ -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) {

@ -39,6 +39,9 @@ import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory;
@Configuration
public class DubboOpenFeignAutoConfiguration {
/**
* OpenFeign Targeter class name.
*/
public static final String TARGETER_CLASS_NAME = "org.springframework.cloud.openfeign.Targeter";
@Bean

@ -105,10 +105,19 @@ import com.netflix.discovery.shared.Applications;
DubboServiceRegistrationAutoConfiguration.class })
public class DubboServiceDiscoveryAutoConfiguration {
/**
* ZookeeperDiscoveryAutoConfiguration.
*/
public static final String ZOOKEEPER_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryAutoConfiguration";
/**
* ConsulDiscoveryClientConfiguration.
*/
public static final String CONSUL_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.consul.discovery.ConsulDiscoveryClientConfiguration";
/**
* NacosDiscoveryAutoConfiguration.
*/
public static final String NACOS_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME = "com.alibaba.cloud.nacos.NacosDiscoveryAutoConfiguration";
private final DubboServiceMetadataRepository dubboServiceMetadataRepository;

@ -73,8 +73,7 @@ public class RequestMetadata {
}
/**
* Get the best matched {@link RequestMetadata} via specified {@link RequestMetadata}
*
* Get the best matched {@link RequestMetadata} via specified {@link RequestMetadata}.
* @param requestMetadataMap the source of {@link NavigableMap}
* @param requestMetadata the match object
* @return if not matched, return <code>null</code>

@ -27,7 +27,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Method Request Metadata
* Method Request Metadata.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/

@ -21,7 +21,7 @@ import java.util.Set;
import com.fasterxml.jackson.annotation.JsonInclude;
/**
* Service Rest Metadata
* Service Rest Metadata.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @see RestMethodMetadata

@ -88,13 +88,13 @@ public class DubboServiceMetadataRepository
/**
* The {@link URL URLs} property name of {@link DubboMetadataService} :
* "dubbo.metadata-service.urls"
* "dubbo.metadata-service.urls".
*/
public static final String DUBBO_METADATA_SERVICE_URLS_PROPERTY_NAME = DUBBO_METADATA_SERVICE_PREFIX
+ "urls";
/**
* The {@link String#format(String, Object...) pattern} of dubbo protocols port
* The {@link String#format(String, Object...) pattern} of dubbo protocols port.
*/
public static final String DUBBO_PROTOCOLS_PORT_PROPERTY_NAME_PATTERN = "dubbo.protocols.%s.port";
@ -103,17 +103,19 @@ public class DubboServiceMetadataRepository
private final ObjectMapper objectMapper = new ObjectMapper();
/**
* Monitor object for synchronization
* Monitor object for synchronization.
*/
private final Object monitor = new Object();
/**
* A {@link Set} of service names that had been initialized
* A {@link Set} of service names that had been initialized.
*/
private final Set<String> initializedServices = new LinkedHashSet<>();
/**
* All exported {@link URL urls} {@link Map} whose key is the return value of
* {@link URL#getServiceKey()} method and value is the {@link List} of {@link URL
* URLs}
* URLs}.
*/
private final MultiValueMap<String, URL> allExportedURLs = new LinkedMultiValueMap<>();
@ -122,7 +124,7 @@ public class DubboServiceMetadataRepository
/**
* The subscribed {@link URL urls} {@link Map} of {@link DubboMetadataService}, whose
* key is the return value of {@link URL#getServiceKey()} method and value is the
* {@link List} of {@link URL URLs}
* {@link List} of {@link URL URLs}.
*/
private final MultiValueMap<String, URL> subscribedDubboMetadataServiceURLs = new LinkedMultiValueMap<>();
@ -145,8 +147,10 @@ public class DubboServiceMetadataRepository
// =================================== REST Metadata
// ================================== //
private volatile Set<String> subscribedServices = emptySet();
/**
* Key is application name Value is Map<RequestMetadata, DubboRestServiceMetadata>
* Key is application name Value is Map&lt;RequestMetadata,
* DubboRestServiceMetadata&gt;.
*/
private Map<String, Map<RequestMetadataMatcher, DubboRestServiceMetadata>> dubboRestServiceMetadataRepository = newHashMap();
@ -165,6 +169,9 @@ public class DubboServiceMetadataRepository
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private MetadataServiceInstanceSelector metadataServiceInstanceSelector;
@Autowired
private JSONUtils jsonUtils;
@ -199,9 +206,8 @@ public class DubboServiceMetadataRepository
}
/**
* Initialize {@link #subscribedServices the subscribed services}
*
* @return
* Initialize {@link #subscribedServices the subscribed services}.
* @return stream of subscribed services
*/
@PostConstruct
public Stream<String> initSubscribedServices() {
@ -253,7 +259,7 @@ public class DubboServiceMetadataRepository
}
/**
* Initialize the metadata
* Initialize the metadata.
*/
private void initializeMetadata() {
doGetSubscribedServices().forEach(this::initializeMetadata);
@ -263,7 +269,8 @@ public class DubboServiceMetadataRepository
}
/**
* Initialize the metadata of Dubbo Services
* Initialize the metadata of Dubbo Services.
* @param serviceName service of name
*/
public void initializeMetadata(String serviceName) {
synchronized (monitor) {
@ -281,9 +288,7 @@ public class DubboServiceMetadataRepository
serviceName);
}
// Keep the order in following invocations
initSubscribedDubboMetadataService(serviceName);
initDubboRestServiceMetadataRepository(serviceName);
// mark this service name having been initialized
initializedServices.add(serviceName);
}
@ -291,7 +296,7 @@ public class DubboServiceMetadataRepository
}
/**
* Remove the metadata of Dubbo Services if no there is no service instance
* Remove the metadata of Dubbo Services if no there is no service instance.
* @param serviceName the service name
*/
public void removeInitializedService(String serviceName) {
@ -301,8 +306,7 @@ public class DubboServiceMetadataRepository
}
/**
* Get the metadata {@link Map} of {@link DubboMetadataService}
*
* Get the metadata {@link Map} of {@link DubboMetadataService}.
* @return non-null read-only {@link Map}
*/
public Map<String, String> getDubboMetadataServiceMetadata() {
@ -342,8 +346,7 @@ public class DubboServiceMetadataRepository
}
/**
* Get the property name of Dubbo Protocol
*
* Get the property name of Dubbo Protocol.
* @param protocol Dubbo Protocol
* @return non-null
*/
@ -352,8 +355,7 @@ public class DubboServiceMetadataRepository
}
/**
* Publish the {@link Set} of {@link ServiceRestMetadata}
*
* Publish the {@link Set} of {@link ServiceRestMetadata}.
* @param serviceRestMetadataSet the {@link Set} of {@link ServiceRestMetadata}
*/
public void publishServiceRestMetadata(
@ -366,8 +368,7 @@ public class DubboServiceMetadataRepository
}
/**
* Get the {@link Set} of {@link ServiceRestMetadata}
*
* Get the {@link Set} of {@link ServiceRestMetadata}.
* @return non-null read-only {@link Set}
*/
public Set<ServiceRestMetadata> getServiceRestMetadata() {
@ -396,10 +397,9 @@ public class DubboServiceMetadataRepository
}
/**
* The specified service is subscribe or not
*
* The specified service is subscribe or not.
* @param serviceName the service name
* @return
* @return subscribe or not
*/
public boolean isSubscribedService(String serviceName) {
return doGetSubscribedServices().contains(serviceName);
@ -431,7 +431,6 @@ public class DubboServiceMetadataRepository
/**
* Get all exported {@link URL urls}.
*
* @return non-null read-only
*/
public Map<String, List<URL>> getAllExportedUrls() {
@ -439,8 +438,7 @@ public class DubboServiceMetadataRepository
}
/**
* Get all exported {@link URL#getServiceKey() service keys}
*
* Get all exported {@link URL#getServiceKey() service keys}.
* @return non-null read-only
*/
public Set<String> getAllServiceKeys() {
@ -449,8 +447,7 @@ public class DubboServiceMetadataRepository
/**
* Get the {@link URL urls} that {@link DubboMetadataService} exported by the
* specified {@link ServiceInstance}
*
* specified {@link ServiceInstance}.
* @param serviceInstance {@link ServiceInstance}
* @return the mutable {@link URL urls}
*/
@ -475,8 +472,7 @@ public class DubboServiceMetadataRepository
}
/**
* Initialize the specified service's {@link ServiceRestMetadata}
*
* Initialize the specified service's {@link ServiceRestMetadata}.
* @param serviceName the service name
*/
protected void initDubboRestServiceMetadataRepository(String serviceName) {
@ -521,8 +517,7 @@ public class DubboServiceMetadataRepository
/**
* Get a {@link DubboRestServiceMetadata} by the specified service name if
* {@link RequestMetadata} matched
*
* {@link RequestMetadata} matched.
* @param serviceName service name
* @param requestMetadata {@link RequestMetadata} to be matched
* @return {@link DubboRestServiceMetadata} if matched, or <code>null</code>
@ -618,7 +613,7 @@ public class DubboServiceMetadataRepository
}
protected void initSubscribedDubboMetadataService(String serviceName) {
discoveryClient.getInstances(serviceName).stream().findAny()
metadataServiceInstanceSelector.choose(discoveryClient.getInstances(serviceName))
.map(this::getDubboMetadataServiceURLs)
.ifPresent(dubboMetadataServiceURLs -> {
dubboMetadataServiceURLs.forEach(dubboMetadataServiceURL -> {
@ -634,6 +629,7 @@ public class DubboServiceMetadataRepository
}
});
});
initDubboRestServiceMetadataRepository(serviceName);
}
private void initSubscribedDubboMetadataServiceURL(URL dubboMetadataServiceURL) {
@ -649,8 +645,9 @@ public class DubboServiceMetadataRepository
dubboMetadataConfigServiceProxy.initProxy(serviceName, version);
}
public void removeServiceMetadata(String serviceName) {
public void removeMetadata(String serviceName) {
dubboRestServiceMetadataRepository.remove(serviceName);
subscribedDubboMetadataServiceURLs.remove(serviceName);
}
@Override
@ -658,4 +655,5 @@ public class DubboServiceMetadataRepository
ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}

@ -0,0 +1,38 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.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);
}

@ -59,7 +59,7 @@ public class DubboServiceBeanMetadataResolver
private ClassLoader classLoader;
/**
* Feign Contracts
* Feign Contracts.
*/
private Collection<Contract> contracts;
@ -153,9 +153,8 @@ public class DubboServiceBeanMetadataResolver
* Select feign contract methods
* <p>
* extract some code from
* {@link Contract.BaseContract#parseAndValidatateMetadata(java.lang.Class)}
*
* @param targetType
* {@link Contract.BaseContract#parseAndValidatateMetadata(java.lang.Class)}.
* @param targetType class of type
* @return non-null
*/
private List<Method> selectFeignContractMethods(Class<?> targetType) {

@ -23,25 +23,24 @@ import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata;
import com.alibaba.cloud.dubbo.metadata.ServiceRestMetadata;
/**
* The REST metadata resolver
* The REST metadata resolver.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public interface MetadataResolver {
/**
* Resolve the {@link ServiceRestMetadata} {@link Set set} from {@link ServiceBean}
*
* Resolve the {@link ServiceRestMetadata} {@link Set set} from {@link ServiceBean}.
* @param serviceBean {@link ServiceBean}
* @return non-null {@link Set}
*/
Set<ServiceRestMetadata> resolveServiceRestMetadata(ServiceBean serviceBean);
/**
* Resolve {@link RestMethodMetadata} {@link Set set} from {@link Class target type}
*
* Resolve {@link RestMethodMetadata} {@link Set set} from {@link Class target type}.
* @param targetType {@link Class target type}
* @return non-null {@link Set}
*/
Set<RestMethodMetadata> resolveMethodRestMetadata(Class<?> targetType);
}

@ -13,19 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.dubbo.registry;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.apache.dubbo.common.URLBuilder.from;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE;
import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
import static org.apache.dubbo.registry.Constants.ADMIN_PROTOCOL;
import static org.springframework.util.StringUtils.hasText;
package com.alibaba.cloud.dubbo.registry;
import java.util.Collection;
import java.util.HashSet;
@ -35,48 +24,62 @@ import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.registry.event.ServiceInstancesChangedEvent;
import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory;
import com.alibaba.cloud.dubbo.service.DubboMetadataService;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy;
import com.alibaba.cloud.dubbo.util.JSONUtils;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.RegistryFactory;
import org.apache.dubbo.registry.support.FailbackRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.CollectionUtils;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.registry.event.ServiceInstancesChangedEvent;
import com.alibaba.cloud.dubbo.service.DubboMetadataService;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy;
import com.alibaba.cloud.dubbo.util.JSONUtils;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.apache.dubbo.common.URLBuilder.from;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE;
import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
import static org.apache.dubbo.registry.Constants.ADMIN_PROTOCOL;
import static org.springframework.util.StringUtils.hasText;
/**
* Abstract Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration
* abstraction, whose protocol is "spring-cloud"
* abstraction, whose protocol is "spring-cloud".
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
/**
* The parameter name of {@link #servicesLookupInterval}
* The parameter name of {@link #servicesLookupInterval}.
*/
public static final String SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.services.lookup.interval";
protected static final String DUBBO_METADATA_SERVICE_CLASS_NAME = DubboMetadataService.class
.getName();
/**
* Caches the IDs of {@link ApplicationListener}
* Caches the IDs of {@link ApplicationListener}.
*/
private static final Set<String> registerListeners = new HashSet<>();
protected final Logger logger = LoggerFactory.getLogger(getClass());
/**
* The interval in second of lookup service names(only for Dubbo-OPS)
* The interval in second of lookup service names(only for Dubbo-OPS).
*/
private final long servicesLookupInterval;
@ -88,12 +91,15 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
private final JSONUtils jsonUtils;
private final DubboGenericServiceFactory dubboGenericServiceFactory;
private final ConfigurableApplicationContext applicationContext;
public AbstractSpringCloudRegistry(URL url, DiscoveryClient discoveryClient,
DubboServiceMetadataRepository dubboServiceMetadataRepository,
DubboMetadataServiceProxy dubboMetadataConfigServiceProxy,
JSONUtils jsonUtils, ConfigurableApplicationContext applicationContext) {
JSONUtils jsonUtils, DubboGenericServiceFactory dubboGenericServiceFactory,
ConfigurableApplicationContext applicationContext) {
super(url);
this.servicesLookupInterval = url
.getParameter(SERVICES_LOOKUP_INTERVAL_PARAM_NAME, 60L);
@ -101,6 +107,7 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
this.repository = dubboServiceMetadataRepository;
this.dubboMetadataConfigServiceProxy = dubboMetadataConfigServiceProxy;
this.jsonUtils = jsonUtils;
this.dubboGenericServiceFactory = dubboGenericServiceFactory;
this.applicationContext = applicationContext;
}
@ -127,8 +134,7 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
}
/**
* The sub-type should implement to register
*
* The sub-type should implement to register.
* @param url {@link URL}
*/
protected abstract void doRegister0(URL url);
@ -142,8 +148,7 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
}
/**
* The sub-type should implement to unregister
*
* The sub-type should implement to unregister.
* @param url {@link URL}
*/
protected abstract void doUnregister0(URL url);
@ -171,8 +176,7 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
/**
* Register a {@link ApplicationListener listener} for
* {@link ServiceInstancesChangedEvent}
*
* {@link ServiceInstancesChangedEvent}.
* @param url {@link URL}
* @param listener {@link NotifyListener}
*/
@ -221,7 +225,8 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
if (CollectionUtils.isEmpty(serviceInstances)) {
dubboMetadataConfigServiceProxy.removeProxy(serviceName);
repository.removeInitializedService(serviceName);
repository.removeServiceMetadata(serviceName);
repository.removeMetadata(serviceName);
dubboGenericServiceFactory.destroy(serviceName);
if (logger.isWarnEnabled()) {
logger.warn(
"There is no instance from service[name : {}], and then Dubbo Service[key : {}] will not be "
@ -350,8 +355,8 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
@Override
public final void doUnsubscribe(URL url, NotifyListener listener) {
if (isAdminURL(url)) {
}
// if (isAdminURL(url)) {
// }
}
@Override
@ -366,4 +371,5 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
protected boolean isDubboMetadataServiceURL(URL url) {
return DUBBO_METADATA_SERVICE_CLASS_NAME.equals(url.getServiceInterface());
}
}

@ -31,7 +31,7 @@ class DelegatingRegistration implements Registration {
private final ServiceInstance delegate;
public DelegatingRegistration(ServiceInstance delegate) {
DelegatingRegistration(ServiceInstance delegate) {
this.delegate = delegate;
}
@ -69,4 +69,5 @@ class DelegatingRegistration implements Registration {
public String getScheme() {
return delegate.getScheme();
}
}

@ -13,20 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.dubbo.registry;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy;
import com.alibaba.cloud.dubbo.util.JSONUtils;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.registry.RegistryFactory;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.ConfigurableApplicationContext;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy;
import com.alibaba.cloud.dubbo.util.JSONUtils;
/**
* Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose
* protocol is "spring-cloud"
* protocol is "spring-cloud".
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@ -37,9 +39,11 @@ public class SpringCloudRegistry extends AbstractSpringCloudRegistry {
public SpringCloudRegistry(URL url, DiscoveryClient discoveryClient,
DubboServiceMetadataRepository dubboServiceMetadataRepository,
DubboMetadataServiceProxy dubboMetadataConfigServiceProxy,
JSONUtils jsonUtils, ConfigurableApplicationContext applicationContext) {
JSONUtils jsonUtils, DubboGenericServiceFactory dubboGenericServiceFactory,
ConfigurableApplicationContext applicationContext) {
super(url, discoveryClient, dubboServiceMetadataRepository,
dubboMetadataConfigServiceProxy, jsonUtils, applicationContext);
dubboMetadataConfigServiceProxy, jsonUtils, dubboGenericServiceFactory,
applicationContext);
this.dubboServiceMetadataRepository = dubboServiceMetadataRepository;
}
@ -52,4 +56,5 @@ public class SpringCloudRegistry extends AbstractSpringCloudRegistry {
protected void doUnregister0(URL url) {
dubboServiceMetadataRepository.unexportURL(url);
}
}

@ -13,23 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.dubbo.registry;
import static java.lang.System.getProperty;
package com.alibaba.cloud.dubbo.registry;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy;
import com.alibaba.cloud.dubbo.util.JSONUtils;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.RegistryFactory;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.ConfigurableApplicationContext;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy;
import com.alibaba.cloud.dubbo.util.JSONUtils;
import static java.lang.System.getProperty;
/**
* Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose
* protocol is "spring-cloud"
* protocol is "spring-cloud".
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @see RegistryFactory
@ -37,8 +39,14 @@ import com.alibaba.cloud.dubbo.util.JSONUtils;
*/
public class SpringCloudRegistryFactory implements RegistryFactory {
/**
* Spring Cloud Protocol.
*/
public static String PROTOCOL = "spring-cloud";
/**
* Spring Cloud Address.
*/
public static String ADDRESS = "localhost";
private static String SERVICES_LOOKUP_SCHEDULER_THREAD_NAME_PREFIX = getProperty(
@ -55,6 +63,8 @@ public class SpringCloudRegistryFactory implements RegistryFactory {
private JSONUtils jsonUtils;
private DubboGenericServiceFactory dubboGenericServiceFactory;
private volatile boolean initialized = false;
public SpringCloudRegistryFactory() {
@ -75,6 +85,8 @@ public class SpringCloudRegistryFactory implements RegistryFactory {
this.dubboMetadataConfigServiceProxy = applicationContext
.getBean(DubboMetadataServiceProxy.class);
this.jsonUtils = applicationContext.getBean(JSONUtils.class);
this.dubboGenericServiceFactory = applicationContext
.getBean(DubboGenericServiceFactory.class);
}
@Override
@ -82,6 +94,7 @@ public class SpringCloudRegistryFactory implements RegistryFactory {
init();
return new SpringCloudRegistry(url, discoveryClient,
dubboServiceMetadataRepository, dubboMetadataConfigServiceProxy,
jsonUtils, applicationContext);
jsonUtils, dubboGenericServiceFactory, applicationContext);
}
}

@ -38,7 +38,6 @@ public class SubscribedServicesChangedEvent extends ApplicationEvent {
/**
* Create a new ApplicationEvent.
*
* @param source the object on which the event initially occurred (never {@code null})
* @param oldSubscribedServices the subscribed services before changed
* @param newSubscribedServices the subscribed services after changed

@ -13,23 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.dubbo.service;
import static java.util.Collections.emptyMap;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
import static org.springframework.util.StringUtils.commaDelimitedListToStringArray;
package com.alibaba.cloud.dubbo.service;
import java.beans.PropertyEditorSupport;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.PreDestroy;
import com.alibaba.cloud.dubbo.metadata.DubboRestServiceMetadata;
import com.alibaba.cloud.dubbo.metadata.ServiceRestMetadata;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.config.RegistryConfig;
@ -37,6 +37,7 @@ import org.apache.dubbo.config.spring.ReferenceBean;
import org.apache.dubbo.rpc.service.GenericService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
@ -44,11 +45,13 @@ import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.util.StringUtils;
import org.springframework.validation.DataBinder;
import com.alibaba.cloud.dubbo.metadata.DubboRestServiceMetadata;
import com.alibaba.cloud.dubbo.metadata.ServiceRestMetadata;
import static java.util.Collections.emptyMap;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
import static org.springframework.util.StringUtils.commaDelimitedListToStringArray;
/**
* Dubbo {@link GenericService} Factory
* Dubbo {@link GenericService} Factory.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@ -56,7 +59,7 @@ public class DubboGenericServiceFactory {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final ConcurrentMap<Integer, ReferenceBean<GenericService>> cache = new ConcurrentHashMap<>();
private final ConcurrentMap<String, ReferenceBean<GenericService>> cache = new ConcurrentHashMap<>();
@Autowired
private ObjectProvider<List<RegistryConfig>> registryConfigs;
@ -95,12 +98,13 @@ public class DubboGenericServiceFactory {
Integer key = Objects.hash(interfaceName, version, group,
dubboTranslatedAttributes);
return cache.computeIfAbsent(key, k -> {
return cache.computeIfAbsent(group + key, k -> {
ReferenceBean<GenericService> referenceBean = new ReferenceBean<>();
referenceBean.setGeneric(true);
referenceBean.setInterface(interfaceName);
referenceBean.setVersion(version);
referenceBean.setGroup(group);
referenceBean.setCheck(false);
bindReferenceBean(referenceBean, dubboTranslatedAttributes);
return referenceBean;
});
@ -148,7 +152,17 @@ public class DubboGenericServiceFactory {
@PreDestroy
public void destroy() {
destroyReferenceBeans();
cache.values();
cache.clear();
}
public synchronized void destroy(String serviceName) {
Set<String> removeGroups = new HashSet<>(cache.keySet());
for (String key : removeGroups) {
if (key.contains(serviceName)) {
ReferenceBean<GenericService> referenceBean = cache.remove(key);
referenceBean.destroy();
}
}
}
private void destroyReferenceBeans() {

@ -24,7 +24,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link DubboMetadataService} {@link InvocationHandler}
* {@link DubboMetadataService} {@link InvocationHandler}.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@ -34,7 +34,7 @@ class DubboMetadataServiceInvocationHandler implements InvocationHandler {
private final GenericService genericService;
public DubboMetadataServiceInvocationHandler(String serviceName, String version,
DubboMetadataServiceInvocationHandler(String serviceName, String version,
DubboGenericServiceFactory dubboGenericServiceFactory) {
this.genericService = dubboGenericServiceFactory.create(serviceName,
DubboMetadataService.class, version);
@ -60,4 +60,5 @@ class DubboMetadataServiceInvocationHandler implements InvocationHandler {
return Stream.of(parameterTypes).map(Class::getName)
.toArray(length -> new String[length]);
}
}

@ -15,23 +15,25 @@
*/
package com.alibaba.cloud.dubbo.service;
import static java.lang.reflect.Proxy.newProxyInstance;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.DisposableBean;
import static java.lang.reflect.Proxy.newProxyInstance;
/**
* The proxy of {@link DubboMetadataService}
* The proxy of {@link DubboMetadataService}.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboMetadataServiceProxy implements BeanClassLoaderAware, DisposableBean {
private final DubboGenericServiceFactory dubboGenericServiceFactory;
private final Map<String, DubboMetadataService> dubboMetadataServiceCache = new ConcurrentHashMap<>();
private ClassLoader classLoader;
public DubboMetadataServiceProxy(
@ -40,8 +42,7 @@ public class DubboMetadataServiceProxy implements BeanClassLoaderAware, Disposab
}
/**
* Initializes {@link DubboMetadataService}'s Proxy
*
* Initializes {@link DubboMetadataService}'s Proxy.
* @param serviceName the service name
* @param version the service version
* @return a {@link DubboMetadataService} proxy
@ -52,7 +53,7 @@ public class DubboMetadataServiceProxy implements BeanClassLoaderAware, Disposab
}
/**
* Remove {@link DubboMetadataService}'s Proxy by service name
* Remove {@link DubboMetadataService}'s Proxy by service name.
* @param serviceName the service name
*/
public void removeProxy(String serviceName) {
@ -60,8 +61,8 @@ public class DubboMetadataServiceProxy implements BeanClassLoaderAware, Disposab
}
/**
* Get a proxy instance of {@link DubboMetadataService} via the specified service name
*
* Get a proxy instance of {@link DubboMetadataService} via the specified service
* name.
* @param serviceName the service name
* @return a {@link DubboMetadataService} proxy
*/
@ -80,8 +81,8 @@ public class DubboMetadataServiceProxy implements BeanClassLoaderAware, Disposab
}
/**
* New a proxy instance of {@link DubboMetadataService} via the specified service name
*
* New a proxy instance of {@link DubboMetadataService} via the specified service
* name.
* @param serviceName the service name
* @param version the service version
* @return a {@link DubboMetadataService} proxy
@ -92,4 +93,5 @@ public class DubboMetadataServiceProxy implements BeanClassLoaderAware, Disposab
new DubboMetadataServiceInvocationHandler(serviceName, version,
dubboGenericServiceFactory));
}
}

@ -15,13 +15,14 @@
*/
package com.alibaba.cloud.dubbo.service.parameter;
import org.apache.dubbo.rpc.service.GenericService;
import org.springframework.core.Ordered;
import com.alibaba.cloud.dubbo.http.HttpServerRequest;
import com.alibaba.cloud.dubbo.metadata.MethodParameterMetadata;
import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata;
import org.apache.dubbo.rpc.service.GenericService;
import org.springframework.core.Ordered;
/**
* Dubbo {@link GenericService} Parameter Resolver
*
@ -31,8 +32,10 @@ public interface DubboGenericServiceParameterResolver extends Ordered {
/**
* Resolves a method parameter into an argument value from a given request.
*
* @return
* @param restMethodMetadata method request metadata
* @param methodParameterMetadata metadata of method
* @param request Http server request
* @return the result of resolve
*/
Object resolve(RestMethodMetadata restMethodMetadata,
MethodParameterMetadata methodParameterMetadata, HttpServerRequest request);

@ -4,7 +4,7 @@ transport {
#NIO NATIVE
server = "NIO"
#enable heartbeat
heartbeat = true
heartbeat = false
#thread factory for netty
thread-factory {
boss-thread-prefix = "NettyBoss"
@ -46,17 +46,22 @@ client {
retry.times = 30
}
report.retry.count = 5
tm.commit.retry.count = 1
tm.rollback.retry.count = 1
}
transaction {
undo.data.validation = true
undo.log.serialization = "jackson"
undo.log.save.days = 7
#schedule delete expired undo_log in milliseconds
undo.log.delete.period = 86400000
undo.log.table = "undo_log"
}
## metrics settings
metrics {
enabled = false
registry-type = "compact"
# multi exporters use comma divided
exporter-list = "prometheus"
exporter-prometheus-port = 9898
support {
## spring
spring {
# auto proxy the DataSource bean
datasource.autoproxy = false
}
}

@ -4,7 +4,7 @@ registry {
nacos {
serverAddr = "localhost"
namespace = "public"
namespace = ""
cluster = "default"
}
eureka {
@ -50,8 +50,7 @@ config {
nacos {
serverAddr = "localhost"
namespace = "public"
cluster = "default"
namespace = ""
}
consul {
serverAddr = "127.0.0.1:8500"

@ -4,7 +4,7 @@ transport {
#NIO NATIVE
server = "NIO"
#enable heartbeat
heartbeat = true
heartbeat = false
#thread factory for netty
thread-factory {
boss-thread-prefix = "NettyBoss"
@ -46,17 +46,22 @@ client {
retry.times = 30
}
report.retry.count = 5
tm.commit.retry.count = 1
tm.rollback.retry.count = 1
}
transaction {
undo.data.validation = true
undo.log.serialization = "jackson"
undo.log.save.days = 7
#schedule delete expired undo_log in milliseconds
undo.log.delete.period = 86400000
undo.log.table = "undo_log"
}
## metrics settings
metrics {
enabled = false
registry-type = "compact"
# multi exporters use comma divided
exporter-list = "prometheus"
exporter-prometheus-port = 9898
support {
## spring
spring {
# auto proxy the DataSource bean
datasource.autoproxy = false
}
}

@ -4,7 +4,7 @@ registry {
nacos {
serverAddr = "localhost"
namespace = "public"
namespace = ""
cluster = "default"
}
eureka {
@ -50,8 +50,7 @@ config {
nacos {
serverAddr = "localhost"
namespace = "public"
cluster = "default"
namespace = ""
}
consul {
serverAddr = "127.0.0.1:8500"

@ -4,7 +4,7 @@ transport {
#NIO NATIVE
server = "NIO"
#enable heartbeat
heartbeat = true
heartbeat = false
#thread factory for netty
thread-factory {
boss-thread-prefix = "NettyBoss"
@ -46,17 +46,22 @@ client {
retry.times = 30
}
report.retry.count = 5
tm.commit.retry.count = 1
tm.rollback.retry.count = 1
}
transaction {
undo.data.validation = true
undo.log.serialization = "jackson"
undo.log.save.days = 7
#schedule delete expired undo_log in milliseconds
undo.log.delete.period = 86400000
undo.log.table = "undo_log"
}
## metrics settings
metrics {
enabled = false
registry-type = "compact"
# multi exporters use comma divided
exporter-list = "prometheus"
exporter-prometheus-port = 9898
support {
## spring
spring {
# auto proxy the DataSource bean
datasource.autoproxy = false
}
}

@ -4,7 +4,7 @@ registry {
nacos {
serverAddr = "localhost"
namespace = "public"
namespace = ""
cluster = "default"
}
eureka {
@ -50,8 +50,7 @@ config {
nacos {
serverAddr = "localhost"
namespace = "public"
cluster = "default"
namespace = ""
}
consul {
serverAddr = "127.0.0.1:8500"

@ -99,12 +99,16 @@ CREATE TABLE `account_tbl` (
点击这个页面 [https://github.com/seata/seata/releases](https://github.com/seata/seata/releases),下载最新版本的 Seata Server 端.
进入解压之后的 bin 目录,执行如下命令来启动
进入解压之后的 bin 目录,执行如下命令来启动, 所有启动参数为可选项。
```$shell
sh seata-server.sh -p $LISTEN_PORT -m $MODE(file or db)
sh seata-server.sh -p $LISTEN_PORT -m $MODE(file or db) -h $HOST -e $ENV
```
-p seata-server 监听服务端口号
-m 存储模式可选值file、db。file 用于单点模式db用于ha模式当使用db存储模式需要修改配置中store配置节点的数据库配置同时在数据库中初始化[global_table、branch_table和
lock_table](https://github.com/seata/seata/blob/develop/server/src/main/resources/db_store.sql)
-h 用于解决seata-server和业务侧跨网络问题其配置的host值直接显示到注册中心的服务可用地址host当跨网络时这里需要配置为公网IP或NATIP若都在同一局域网则无需配置
-e 用于解决多环境配置中心隔离问题
在这个示例中,采用如下命令来启动 Seata Server
```$shell

@ -4,7 +4,7 @@ transport {
#NIO NATIVE
server = "NIO"
#enable heartbeat
heartbeat = true
heartbeat = false
#thread factory for netty
thread-factory {
boss-thread-prefix = "NettyBoss"
@ -46,17 +46,22 @@ client {
retry.times = 30
}
report.retry.count = 5
tm.commit.retry.count = 1
tm.rollback.retry.count = 1
}
transaction {
undo.data.validation = true
undo.log.serialization = "jackson"
undo.log.save.days = 7
#schedule delete expired undo_log in milliseconds
undo.log.delete.period = 86400000
undo.log.table = "undo_log"
}
## metrics settings
metrics {
enabled = false
registry-type = "compact"
# multi exporters use comma divided
exporter-list = "prometheus"
exporter-prometheus-port = 9898
support {
## spring
spring {
# auto proxy the DataSource bean
datasource.autoproxy = false
}
}

@ -4,7 +4,7 @@ registry {
nacos {
serverAddr = "localhost"
namespace = "public"
namespace = ""
cluster = "default"
}
eureka {
@ -50,8 +50,7 @@ config {
nacos {
serverAddr = "localhost"
namespace = "public"
cluster = "default"
namespace = ""
}
consul {
serverAddr = "127.0.0.1:8500"

@ -25,6 +25,7 @@
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>sentinel-dubbo-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>

@ -25,6 +25,7 @@
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>sentinel-dubbo-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>

@ -14,29 +14,41 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.nacos;
import java.util.Objects;
import com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureException;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author <a href="mailto:liaochunyhm@live.com">liaochuntao</a>
*/
public class NacosConfigManager implements ApplicationContextAware {
public class NacosConfigManager {
private static ConfigService service = null;
private ConfigService configService;
@Autowired
private NacosConfigProperties properties;
public ConfigService getConfigService() {
return configService;
if (Objects.isNull(service)) {
try {
service = NacosFactory
.createConfigService(properties.getConfigServiceProperties());
properties.initConfigService(service);
}
catch (NacosException e) {
throw new NacosConnectionFailureException(properties.getServerAddr(),
e.getMessage(), e);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
NacosConfigProperties properties = applicationContext
.getBean(NacosConfigProperties.class);
configService = properties.configServiceInstance();
}
return service;
}
}

@ -16,38 +16,37 @@
package com.alibaba.cloud.nacos;
import static com.alibaba.nacos.api.PropertyKeyConst.ACCESS_KEY;
import static com.alibaba.nacos.api.PropertyKeyConst.CLUSTER_NAME;
import static com.alibaba.nacos.api.PropertyKeyConst.CONTEXT_PATH;
import static com.alibaba.nacos.api.PropertyKeyConst.ENCODE;
import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT;
import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT_PORT;
import static com.alibaba.nacos.api.PropertyKeyConst.NAMESPACE;
import static com.alibaba.nacos.api.PropertyKeyConst.SECRET_KEY;
import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR;
import static com.alibaba.nacos.api.PropertyKeyConst.MAX_RETRY;
import static com.alibaba.nacos.api.PropertyKeyConst.CONFIG_LONG_POLL_TIMEOUT;
import static com.alibaba.nacos.api.PropertyKeyConst.CONFIG_RETRY_TIME;
import static com.alibaba.nacos.api.PropertyKeyConst.ENABLE_REMOTE_SYNC_CONFIG;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import javax.annotation.PostConstruct;
import com.alibaba.nacos.api.config.ConfigService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import static com.alibaba.nacos.api.PropertyKeyConst.ACCESS_KEY;
import static com.alibaba.nacos.api.PropertyKeyConst.CLUSTER_NAME;
import static com.alibaba.nacos.api.PropertyKeyConst.CONFIG_LONG_POLL_TIMEOUT;
import static com.alibaba.nacos.api.PropertyKeyConst.CONFIG_RETRY_TIME;
import static com.alibaba.nacos.api.PropertyKeyConst.CONTEXT_PATH;
import static com.alibaba.nacos.api.PropertyKeyConst.ENABLE_REMOTE_SYNC_CONFIG;
import static com.alibaba.nacos.api.PropertyKeyConst.ENCODE;
import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT;
import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT_PORT;
import static com.alibaba.nacos.api.PropertyKeyConst.MAX_RETRY;
import static com.alibaba.nacos.api.PropertyKeyConst.NAMESPACE;
import static com.alibaba.nacos.api.PropertyKeyConst.SECRET_KEY;
import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR;
/**
* nacos properties
* Nacos properties.
*
* @author leijuan
* @author xiaojing
@ -57,6 +56,9 @@ import com.alibaba.nacos.api.config.ConfigService;
@ConfigurationProperties(NacosConfigProperties.PREFIX)
public class NacosConfigProperties {
/**
* Prefix of {@link NacosConfigProperties}.
*/
public static final String PREFIX = "spring.cloud.nacos.config";
private static final Logger log = LoggerFactory
@ -76,7 +78,7 @@ public class NacosConfigProperties {
.resolvePlaceholders("${spring.cloud.nacos.config.server-addr:}");
if (StringUtils.isEmpty(serverAddr)) {
serverAddr = environment
.resolvePlaceholders("${spring.cloud.nacos.server-addr}");
.resolvePlaceholders("${spring.cloud.nacos.server-addr:}");
}
this.setServerAddr(serverAddr);
}
@ -101,6 +103,7 @@ public class NacosConfigProperties {
* nacos config dataId prefix.
*/
private String prefix;
/**
* the suffix of nacos config dataId, also the file extension of config content.
*/
@ -351,15 +354,71 @@ public class NacosConfigProperties {
this.name = name;
}
/**
* @see NacosConfigManager#getConfigService() .
* @return ConfigService
*/
@Deprecated
public ConfigService configServiceInstance() {
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, ""));
properties.put(NAMESPACE, Objects.toString(this.namespace, ""));
properties.put(ACCESS_KEY, Objects.toString(this.accessKey, ""));
properties.put(SECRET_KEY, Objects.toString(this.secretKey, ""));
properties.put(CONTEXT_PATH, Objects.toString(this.contextPath, ""));
properties.put(CLUSTER_NAME, Objects.toString(this.clusterName, ""));
properties.put(MAX_RETRY, Objects.toString(this.maxRetry, ""));
properties.put(CONFIG_LONG_POLL_TIMEOUT,
Objects.toString(this.configLongPollTimeout, ""));
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(":");
properties.put(ENDPOINT, endpoint.substring(0, index));
properties.put(ENDPOINT_PORT, endpoint.substring(index + 1));
}
else {
properties.put(ENDPOINT, endpoint);
}
return properties;
}
@Override
public String toString() {
return "NacosConfigProperties{" + "serverAddr='" + serverAddr + '\''
+ ", encode='" + encode + '\'' + ", group='" + group + '\'' + ", prefix='"
+ prefix + '\'' + ", fileExtension='" + fileExtension + '\''
+ ", timeout=" + timeout + ", endpoint='" + endpoint + '\''
+ ", namespace='" + namespace + '\'' + ", accessKey='" + accessKey + '\''
+ ", secretKey='" + secretKey + '\'' + ", contextPath='" + contextPath
+ '\'' + ", clusterName='" + clusterName + '\'' + ", name='" + name + '\''
+ ", sharedDataids='" + sharedDataids + '\'' + ", refreshableDataids='"
+ refreshableDataids + '\'' + ", extConfig=" + extConfig + '}';
}
public static class Config {
/**
* the data id of extended configuration
* the data id of extended configuration.
*/
private String dataId;
/**
* the group of extended configuration, the default value is DEFAULT_GROUP
* the group of extended configuration, the default value is DEFAULT_GROUP.
*/
private String group = "DEFAULT_GROUP";
/**
* whether to support dynamic refresh, the default does not support .
*/
@ -388,60 +447,7 @@ public class NacosConfigProperties {
public void setRefresh(boolean refresh) {
this.refresh = refresh;
}
}
@Override
public String toString() {
return "NacosConfigProperties{" + "serverAddr='" + serverAddr + '\''
+ ", encode='" + encode + '\'' + ", group='" + group + '\'' + ", prefix='"
+ prefix + '\'' + ", fileExtension='" + fileExtension + '\''
+ ", timeout=" + timeout + ", endpoint='" + endpoint + '\''
+ ", namespace='" + namespace + '\'' + ", accessKey='" + accessKey + '\''
+ ", secretKey='" + secretKey + '\'' + ", contextPath='" + contextPath
+ '\'' + ", clusterName='" + clusterName + '\'' + ", name='" + name + '\''
+ ", sharedDataids='" + sharedDataids + '\'' + ", refreshableDataids='"
+ refreshableDataids + '\'' + ", extConfig=" + extConfig + '}';
}
@Deprecated
public ConfigService configServiceInstance() {
if (null != configService) {
return configService;
}
Properties properties = new Properties();
properties.put(SERVER_ADDR, Objects.toString(this.serverAddr, ""));
properties.put(ENCODE, Objects.toString(this.encode, ""));
properties.put(NAMESPACE, Objects.toString(this.namespace, ""));
properties.put(ACCESS_KEY, Objects.toString(this.accessKey, ""));
properties.put(SECRET_KEY, Objects.toString(this.secretKey, ""));
properties.put(CONTEXT_PATH, Objects.toString(this.contextPath, ""));
properties.put(CLUSTER_NAME, Objects.toString(this.clusterName, ""));
properties.put(MAX_RETRY, Objects.toString(this.maxRetry, ""));
properties.put(CONFIG_LONG_POLL_TIMEOUT,
Objects.toString(this.configLongPollTimeout, ""));
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(":");
properties.put(ENDPOINT, endpoint.substring(0, index));
properties.put(ENDPOINT_PORT, endpoint.substring(index + 1));
}
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;
}
}
}

@ -44,9 +44,13 @@ public class NacosPropertySourceLocator implements PropertySourceLocator {
private static final Logger log = LoggerFactory
.getLogger(NacosPropertySourceLocator.class);
private static final String NACOS_PROPERTY_SOURCE_NAME = "NACOS";
private static final String SEP1 = "-";
private static final String DOT = ".";
private static final String SHARED_CONFIG_SEPARATOR_CHAR = "[,]";
private NacosPropertySourceBuilder nacosPropertySourceBuilder;
@ -156,8 +160,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 +177,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);
return NacosPropertySourceRepository.getNacosPropertySource(dataId);
}
}
else {
ps = nacosPropertySourceBuilder.build(dataId, group, fileExtension, true);
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) {

@ -31,7 +31,10 @@ public class NacosConnectionFailureAnalyzer
@Override
protected FailureAnalysis analyze(Throwable rootFailure,
NacosConnectionFailureException cause) {
return new FailureAnalysis("Application failed to connect to Nacos server",
"check your Nacos server config", cause);
return new FailureAnalysis(
"Application failed to connect to Nacos server: \""
+ cause.getServerAddr() + "\"",
"Please check your Nacos server config", cause);
}
}

@ -24,29 +24,21 @@ package com.alibaba.cloud.nacos.diagnostics.analyzer;
*/
public class NacosConnectionFailureException extends RuntimeException {
private final String domain;
private final String serverAddr;
private final String port;
public NacosConnectionFailureException(String domain, String port, String message) {
public NacosConnectionFailureException(String serverAddr, String message) {
super(message);
this.domain = domain;
this.port = port;
this.serverAddr = serverAddr;
}
public NacosConnectionFailureException(String domain, String port, String message,
public NacosConnectionFailureException(String serverAddr, String message,
Throwable cause) {
super(message, cause);
this.domain = domain;
this.port = port;
}
String getDomain() {
return domain;
this.serverAddr = serverAddr;
}
String getPort() {
return port;
public String getServerAddr() {
return serverAddr;
}
}

@ -32,7 +32,8 @@ import com.alibaba.cloud.nacos.client.NacosPropertySource;
import com.alibaba.cloud.nacos.refresh.NacosRefreshHistory;
/**
* Endpoint for Nacos, contains config data and refresh history
* Endpoint for Nacos, contains config data and refresh history.
*
* @author xiaojing
*/
@Endpoint(id = "nacos-config")

@ -29,9 +29,11 @@ import com.alibaba.nacos.client.utils.StringUtils;
public abstract class AbstractNacosDataParser {
protected static final String DOT = ".";
protected static final String VALUE = "value";
private String extension;
private AbstractNacosDataParser nextParser;
protected AbstractNacosDataParser(String extension) {
@ -41,7 +43,11 @@ public abstract class AbstractNacosDataParser {
this.extension = extension.toLowerCase();
}
/** Verify file extensions */
/**
* Verify dataId extensions.
* @param extension file extension. json or xml or yml or yaml or properties
* @return valid or not
*/
public final boolean checkFileExtension(String extension) {
if (this.isLegal(extension.toLowerCase())) {
return true;
@ -53,7 +59,13 @@ public abstract class AbstractNacosDataParser {
}
/** Parsing nacos configuration content */
/**
* Parsing nacos configuration content.
* @param data config data from Nacos
* @param extension file extension. json or xml or yml or yaml or properties
* @return result of Properties
* @throws IOException thrown if there is a problem parsing config.
*/
public final Properties parseNacosData(String data, String extension)
throws IOException {
if (extension == null || extension.length() < 1) {
@ -68,7 +80,12 @@ public abstract class AbstractNacosDataParser {
return this.nextParser.parseNacosData(data, extension);
}
/** Core logic for parsing */
/**
* Core logic for parsing.
* @param data config from Nacos
* @return result of Properties
* @throws IOException thrown if there is a problem parsing config.
*/
protected abstract Properties doParse(String data) throws IOException;
protected AbstractNacosDataParser setNextParser(AbstractNacosDataParser nextParser) {
@ -76,7 +93,6 @@ public abstract class AbstractNacosDataParser {
return this;
}
/** add the next parser */
public AbstractNacosDataParser addNextParser(AbstractNacosDataParser nextParser) {
if (this.nextParser == null) {
this.nextParser = nextParser;
@ -93,7 +109,7 @@ public abstract class AbstractNacosDataParser {
}
/**
* Generate key-value pairs from the map
* Generate key-value pairs from the map.
*/
protected Properties generateProperties(Map<String, String> map) {
if (null == map || map.isEmpty()) {
@ -112,7 +128,7 @@ public abstract class AbstractNacosDataParser {
}
/**
* Reload the key ending in `value`,if you need
* Reload the key ending in `value` if need.
*/
protected Map<String, String> reloadMap(Map<String, String> map) {
if (map == null || map.isEmpty()) {

@ -44,7 +44,10 @@ public class NacosDataJsonParser extends AbstractNacosDataParser {
}
/**
* JSON to Map
* JSON to Map.
* @param json json data
* @return the map convert by json string
* @throws IOException thrown if there is a problem parsing config.
*/
public static Map<String, String> parseJSON2Map(String json) throws IOException {
Map<String, String> map = new HashMap<>(32);

@ -22,9 +22,7 @@ import java.util.Properties;
/**
* @author zkz
*/
public class NacosDataParserHandler {
private static final NacosDataParserHandler HANDLER = new NacosDataParserHandler();
public final class NacosDataParserHandler {
private AbstractNacosDataParser parser;
@ -32,7 +30,13 @@ public class NacosDataParserHandler {
parser = this.createParser();
}
/** Parsing nacos configuration content */
/**
* Parsing nacos configuration content.
* @param data config from Nacos
* @param extension file extension. json or xml or yml or yaml or properties
* @return result of properties
* @throws IOException thrown if there is a problem parsing config.
*/
public Properties parseNacosData(String data, String extension) throws IOException {
if (null == parser) {
parser = this.createParser();
@ -40,7 +44,11 @@ public class NacosDataParserHandler {
return parser.parseNacosData(data, extension);
}
/** check the validity of file extensions in dataid */
/**
* check the validity of file extensions in dataid.
* @param dataIdAry array of dataId
* @return dataId handle success or not
*/
public boolean checkDataId(String... dataIdAry) {
StringBuilder stringBuilder = new StringBuilder();
for (String dataId : dataIdAry) {
@ -68,7 +76,13 @@ public class NacosDataParserHandler {
}
public static NacosDataParserHandler getInstance() {
return HANDLER;
return ParserHandler.HANDLER;
}
private static class ParserHandler {
private static final NacosDataParserHandler HANDLER = new NacosDataParserHandler();
}
}

@ -25,17 +25,17 @@ import java.util.Properties;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import com.alibaba.nacos.client.utils.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import com.alibaba.nacos.client.utils.StringUtils;
/**
* With relatively few usage scenarios, only simple parsing is performed to reduce jar
* dependencies
* dependencies.
*
* @author zkz
*/
@ -55,9 +55,7 @@ public class NacosDataXmlParser extends AbstractNacosDataParser {
}
private Map<String, String> parseXml2Map(String xml) throws IOException {
xml = xml.replaceAll("\\r", "")
.replaceAll("\\n", "")
.replaceAll("\\t", "");
xml = xml.replaceAll("\\r", "").replaceAll("\\n", "").replaceAll("\\t", "");
Map<String, String> map = new HashMap<>(32);
try {
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance()

@ -109,7 +109,7 @@ public class NacosDiscoveryProperties {
private String clusterName = "DEFAULT";
/**
* group name for nacos
* group name for nacos.
*/
private String group = "DEFAULT_GROUP";
@ -430,7 +430,7 @@ public class NacosDiscoveryProperties {
String serverAddr = env
.resolvePlaceholders("${spring.cloud.nacos.discovery.server-addr:}");
if (StringUtils.isEmpty(serverAddr)) {
serverAddr = env.resolvePlaceholders("${spring.cloud.nacos.server-addr}");
serverAddr = env.resolvePlaceholders("${spring.cloud.nacos.server-addr:}");
}
this.setServerAddr(serverAddr);
}

@ -16,34 +16,36 @@
*/
package com.alibaba.cloud.nacos;
import java.util.Objects;
import com.alibaba.nacos.api.naming.NamingMaintainService;
import com.alibaba.nacos.api.naming.NamingService;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author <a href="mailto:liaochunyhm@live.com">liaochuntao</a>
*/
public class NacosNamingManager implements ApplicationContextAware {
public class NacosNamingManager {
private static NamingService namingService = null;
private static NamingMaintainService namingMaintainService = null;
private NamingService namingService;
private NamingMaintainService namingMaintainService;
@Autowired
private NacosDiscoveryProperties discoveryProperties;
public NamingService getNamingService() {
if (Objects.isNull(namingService)) {
namingService = discoveryProperties.namingServiceInstance();
}
return namingService;
}
public NamingMaintainService getNamingMaintainService() {
return namingMaintainService;
if (Objects.isNull(namingMaintainService)) {
namingMaintainService = discoveryProperties.namingMaintainServiceInstance();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
NacosDiscoveryProperties properties = applicationContext
.getBean(NacosDiscoveryProperties.class);
namingService = properties.namingServiceInstance();
namingMaintainService = properties.namingMaintainServiceInstance();
return namingMaintainService;
}
}

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

@ -1,3 +1,19 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.nacos.ribbon;
import java.util.List;

@ -1,3 +1,19 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.nacos.ribbon;
import java.util.List;

@ -58,6 +58,7 @@ public class SeataHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy
private static class SeataContextCallable<K> implements Callable<K> {
private final Callable<K> actual;
private final String xid;
SeataContextCallable(Callable<K> actual) {

@ -38,48 +38,48 @@ import com.alibaba.csp.sentinel.slots.system.SystemRule;
public enum RuleType {
/**
* flow
* flow.
*/
FLOW("flow", FlowRule.class),
/**
* degrade
* degrade.
*/
DEGRADE("degrade", DegradeRule.class),
/**
* param flow
* param flow.
*/
PARAM_FLOW("param-flow", ParamFlowRule.class),
/**
* system
* system.
*/
SYSTEM("system", SystemRule.class),
/**
* authority
* authority.
*/
AUTHORITY("authority", AuthorityRule.class),
/**
* gateway flow
* gateway flow.
*/
GW_FLOW("gw-flow",
"com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule"),
/**
* api
* api.
*/
GW_API_GROUP("gw-api-group",
"com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition");
/**
* alias for {@link AbstractRule}
* alias for {@link AbstractRule}.
*/
private final String name;
/**
* concrete {@link AbstractRule} class
* concrete {@link AbstractRule} class.
*/
private Class clazz;
/**
* concrete {@link AbstractRule} class name
* concrete {@link AbstractRule} class name.
*/
private String clazzName;

@ -1,26 +0,0 @@
/*
* 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.sentinel.datasource;
/**
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
public interface SentinelDataSourceConstants {
String PROPERTY_PREFIX = "spring.cloud.sentinel";
}

@ -42,11 +42,15 @@ public class AbstractDataSourceProperties {
@NotEmpty
private String dataType = "json";
@NotNull
private RuleType ruleType;
private String converterClass;
@JsonIgnore
private final String factoryBeanName;
@JsonIgnore
private Environment env;

@ -30,8 +30,10 @@ public class ApolloDataSourceProperties extends AbstractDataSourceProperties {
@NotEmpty
private String namespaceName;
@NotEmpty
private String flowRulesKey;
private String defaultFlowRuleValue;
public ApolloDataSourceProperties() {

@ -35,8 +35,11 @@ public class FileDataSourceProperties extends AbstractDataSourceProperties {
@NotEmpty
private String file;
private String charset = "utf-8";
private long recommendRefreshMs = 3000L;
private int bufSize = 1024 * 1024;
public FileDataSourceProperties() {

@ -39,8 +39,11 @@ public class NacosDataSourceProperties extends AbstractDataSourceProperties {
private String dataId;
private String endpoint;
private String namespace;
private String accessKey;
private String secretKey;
public NacosDataSourceProperties() {

@ -14,15 +14,15 @@
* limitations under the License.
*/
package com.alibaba.cloud.sentinel.datasource.config;
import com.alibaba.cloud.sentinel.datasource.factorybean.RedisDataSourceFactoryBean;
import org.springframework.util.StringUtils;
import java.time.Duration;
import java.util.List;
import com.alibaba.cloud.sentinel.datasource.factorybean.RedisDataSourceFactoryBean;
import org.springframework.util.StringUtils;
/**
* Zookeeper Properties class Using by {@link DataSourcePropertiesConfiguration} and
* {@link RedisDataSourceFactoryBean}
@ -36,27 +36,27 @@ public class RedisDataSourceProperties extends AbstractDataSourceProperties {
}
/**
* redis server host
* redis server host.
*/
private String host = "localhost";
/**
* redis server port
* redis server port.
*/
private int port = 6379;
/**
* redis server password
* redis server password.
*/
private String password;
/**
* redis server default select database
* redis server default select database.
*/
private int database;
/**
* redis server timeout
* redis server timeout.
*/
private Duration timeout;
@ -66,17 +66,17 @@ public class RedisDataSourceProperties extends AbstractDataSourceProperties {
private List<String> nodes;
/**
* data key in Redis
* data key in Redis.
*/
private String ruleKey;
/**
* channel to subscribe in Redis
* channel to subscribe in Redis.
*/
private String channel;
/**
* redis sentinel model
* redis sentinel model.
*/
private String masterId;

@ -30,8 +30,11 @@ import com.alibaba.csp.sentinel.datasource.apollo.ApolloDataSource;
public class ApolloDataSourceFactoryBean implements FactoryBean<ApolloDataSource> {
private String namespaceName;
private String flowRulesKey;
private String defaultFlowRuleValue;
private Converter converter;
@Override

@ -34,9 +34,13 @@ public class FileRefreshableDataSourceFactoryBean
implements FactoryBean<FileRefreshableDataSource> {
private String file;
private String charset;
private long recommendRefreshMs;
private int bufSize;
private Converter converter;
@Override

@ -34,13 +34,19 @@ import com.alibaba.nacos.api.PropertyKeyConst;
public class NacosDataSourceFactoryBean implements FactoryBean<NacosDataSource> {
private String serverAddr;
private String groupId;
private String dataId;
private Converter converter;
private String endpoint;
private String namespace;
private String accessKey;
private String secretKey;
@Override

@ -16,17 +16,18 @@
package com.alibaba.cloud.sentinel.datasource.factorybean;
import java.time.Duration;
import java.util.List;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.redis.RedisDataSource;
import com.alibaba.csp.sentinel.datasource.redis.config.RedisConnectionConfig;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.time.Duration;
import java.util.List;
/**
* A {@link FactoryBean} for creating {@link RedisDataSource} instance.
*
@ -51,17 +52,17 @@ public class RedisDataSourceFactoryBean implements FactoryBean<RedisDataSource>
private Converter converter;
/**
* data key in Redis
* data key in Redis.
*/
private String ruleKey;
/**
* channel to subscribe in Redis
* channel to subscribe in Redis.
*/
private String channel;
/**
* redis server password
* redis server password.
*/
private String password;
@ -72,17 +73,18 @@ public class RedisDataSourceFactoryBean implements FactoryBean<RedisDataSource>
RedisConnectionConfig.Builder builder = RedisConnectionConfig.builder();
if (nodes == null || nodes.isEmpty()) {
builder.withHost(host)
.withPort(port)
.withDatabase(database);
} else {
builder.withHost(host).withPort(port).withDatabase(database);
}
else {
nodes.forEach(node -> {
try {
String[] parts = StringUtils.split(node, ":");
Assert.state(parts.length == 2, "Must be defined as 'host:port'");
builder.withRedisSentinel(parts[0], Integer.parseInt(parts[1]));
} catch (RuntimeException ex) {
throw new IllegalStateException("Invalid redis sentinel property " + node, ex);
}
catch (RuntimeException ex) {
throw new IllegalStateException(
"Invalid redis sentinel property " + node, ex);
}
});
builder.withSentinelMasterId(masterId);
@ -96,7 +98,8 @@ public class RedisDataSourceFactoryBean implements FactoryBean<RedisDataSource>
builder.withPassword(password);
}
return new RedisDataSource<List<FlowRule>>(builder.build(), ruleKey, channel, converter);
return new RedisDataSource<List<FlowRule>>(builder.build(), ruleKey, channel,
converter);
}
@Override

@ -35,6 +35,7 @@ public class ZookeeperDataSourceFactoryBean implements FactoryBean<ZookeeperData
private String path;
private String groupId;
private String dataId;
private Converter converter;

@ -15,22 +15,28 @@
*/
package com.alibaba.cloud.sentinel.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.*;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
/**
* @author zhuhonghan
*/
public class GatewayEnvironmentPostProcessor implements EnvironmentPostProcessor {
private final static String SENTINEL_FILTER_ENABLED = "spring.cloud.sentinel.filter.enabled";
private final static String PROPERTY_SOURCE_NAME = "defaultProperties";
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication springApplication) {
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication springApplication) {
addDefaultPropertySource(environment);
}
@ -70,5 +76,4 @@ public class GatewayEnvironmentPostProcessor implements EnvironmentPostProcessor
}
}
}

@ -1,3 +1,19 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.sentinel.custom;
import org.springframework.context.annotation.Configuration;
@ -9,4 +25,5 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration
public class SentinelCircuitBreakerConfiguration {
}

@ -1,3 +1,19 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.sentinel.custom;
import java.lang.reflect.Field;

@ -0,0 +1,43 @@
<?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.1.1.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-alibaba-sidecar</artifactId>
<name>Spring Cloud Alibaba Sidecar</name>
<description>An easy way to integrate polyglot apps for Spring Cloud Alibaba.</description>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

@ -0,0 +1,56 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.sidecar;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.web.client.RestTemplate;
/**
* @author www.itmuch.com
*/
@Configuration
@EnableConfigurationProperties(SidecarProperties.class)
public class SidecarAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public SidecarHealthIndicator sidecarHealthIndicator(
SidecarProperties sidecarProperties, RestTemplate restTemplate) {
return new SidecarHealthIndicator(sidecarProperties, restTemplate);
}
@Bean
public SidecarHealthChecker sidecarHealthChecker(
SidecarDiscoveryClient sidecarDiscoveryClient,
SidecarHealthIndicator sidecarHealthIndicator,
SidecarProperties sidecarProperties, ConfigurableEnvironment environment) {
SidecarHealthChecker cleaner = new SidecarHealthChecker(sidecarDiscoveryClient,
sidecarHealthIndicator, sidecarProperties, environment);
cleaner.check();
return cleaner;
}
}

@ -0,0 +1,40 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.sidecar;
/**
* @author www.itmuch.com
*/
public interface SidecarDiscoveryClient {
/**
* register instance.
* @param applicationName applicationName
* @param ip ip
* @param port port
*/
void registerInstance(String applicationName, String ip, Integer port);
/**
* deregister instance.
* @param applicationName applicationName
* @param ip ip
* @param port port
*/
void deregisterInstance(String applicationName, String ip, Integer port);
}

@ -0,0 +1,77 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.sidecar;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.scheduler.Schedulers;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* @author www.itmuch.com
*/
public class SidecarHealthChecker {
private static final Logger log = LoggerFactory.getLogger(SidecarHealthChecker.class);
private final SidecarDiscoveryClient sidecarDiscoveryClient;
private final HealthIndicator healthIndicator;
private final SidecarProperties sidecarProperties;
private final ConfigurableEnvironment environment;
public SidecarHealthChecker(SidecarDiscoveryClient sidecarDiscoveryClient,
HealthIndicator healthIndicator, SidecarProperties sidecarProperties,
ConfigurableEnvironment environment) {
this.sidecarDiscoveryClient = sidecarDiscoveryClient;
this.healthIndicator = healthIndicator;
this.sidecarProperties = sidecarProperties;
this.environment = environment;
}
public void check() {
Schedulers.single().schedulePeriodically(() -> {
String ip = sidecarProperties.getIp();
Integer port = sidecarProperties.getPort();
Status status = healthIndicator.health().getStatus();
String applicationName = environment.getProperty("spring.application.name");
if (status.equals(Status.UP)) {
this.sidecarDiscoveryClient.registerInstance(applicationName, ip, port);
log.debug(
"Health check success. register this instance. applicationName = {}, ip = {}, port = {}, status = {}",
applicationName, ip, port, status);
}
else {
log.warn(
"Health check failed. unregister this instance. applicationName = {}, ip = {}, port = {}, status = {}",
applicationName, ip, port, status);
this.sidecarDiscoveryClient.deregisterInstance(applicationName, ip, port);
}
}, 0, sidecarProperties.getHealthCheckInterval(), TimeUnit.MILLISECONDS);
}
}

@ -0,0 +1,81 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.sidecar;
import java.net.URI;
import java.util.Map;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
/**
* @author www.itmuch.com
*/
public class SidecarHealthIndicator extends AbstractHealthIndicator {
private final SidecarProperties sidecarProperties;
private final RestTemplate restTemplate;
public SidecarHealthIndicator(SidecarProperties sidecarProperties,
RestTemplate restTemplate) {
this.sidecarProperties = sidecarProperties;
this.restTemplate = restTemplate;
}
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
try {
URI uri = this.sidecarProperties.getHealthCheckUrl();
if (uri == null) {
builder.up();
return;
}
ResponseEntity<Map<String, Object>> exchange = this.restTemplate.exchange(uri,
HttpMethod.GET, null,
new ParameterizedTypeReference<Map<String, Object>>() {
});
Map<String, Object> map = exchange.getBody();
if (map == null) {
this.getWarning(builder);
return;
}
Object status = map.get("status");
if (status instanceof String) {
builder.status(status.toString());
}
else {
this.getWarning(builder);
}
}
catch (Exception e) {
builder.down().withDetail("error", e.getMessage());
}
}
private void getWarning(Health.Builder builder) {
builder.unknown().withDetail("warning", "no status field in response");
}
}

@ -0,0 +1,91 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.sidecar;
import java.net.URI;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
/**
* @author www.itmuch.com
*/
@ConfigurationProperties("sidecar")
@Validated
public class SidecarProperties {
/**
* polyglot service's ip.
*/
private String ip;
/**
* polyglot service's port.
*/
@NotNull
@Max(65535)
@Min(1)
private Integer port;
/**
* polyglot service's health check url. this endpoint must return json and the format
* must follow spring boot actuator's health endpoint. eg. {"status": "UP"}.
*/
private URI healthCheckUrl;
/**
* interval of health check.
*/
private long healthCheckInterval = 30000L;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public URI getHealthCheckUrl() {
return healthCheckUrl;
}
public void setHealthCheckUrl(URI healthCheckUrl) {
this.healthCheckUrl = healthCheckUrl;
}
public long getHealthCheckInterval() {
return healthCheckInterval;
}
public void setHealthCheckInterval(long healthCheckInterval) {
this.healthCheckInterval = healthCheckInterval;
}
}

@ -0,0 +1,73 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.sidecar.consul;
import java.util.List;
import com.alibaba.cloud.sidecar.SidecarAutoConfiguration;
import com.alibaba.cloud.sidecar.SidecarDiscoveryClient;
import com.alibaba.cloud.sidecar.SidecarProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.cloud.consul.discovery.ConsulDiscoveryProperties;
import org.springframework.cloud.consul.discovery.HeartbeatProperties;
import org.springframework.cloud.consul.serviceregistry.ConsulAutoRegistration;
import org.springframework.cloud.consul.serviceregistry.ConsulAutoServiceRegistrationAutoConfiguration;
import org.springframework.cloud.consul.serviceregistry.ConsulManagementRegistrationCustomizer;
import org.springframework.cloud.consul.serviceregistry.ConsulRegistrationCustomizer;
import org.springframework.cloud.consul.serviceregistry.ConsulServiceRegistry;
import org.springframework.cloud.consul.serviceregistry.ConsulServiceRegistryAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author www.itmuch.com
*/
@Configuration
@ConditionalOnClass(ConsulServiceRegistryAutoConfiguration.class)
@AutoConfigureBefore({ ConsulAutoServiceRegistrationAutoConfiguration.class,
SidecarAutoConfiguration.class })
public class SidecarConsulAutoConfiguration {
@Bean
public ConsulAutoRegistration consulRegistration(
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
ConsulDiscoveryProperties properties, ApplicationContext applicationContext,
ObjectProvider<List<ConsulRegistrationCustomizer>> registrationCustomizers,
ObjectProvider<List<ConsulManagementRegistrationCustomizer>> managementRegistrationCustomizers,
HeartbeatProperties heartbeatProperties,
SidecarProperties sidecarProperties) {
return SidecarConsulAutoRegistration.registration(
autoServiceRegistrationProperties, properties, applicationContext,
registrationCustomizers.getIfAvailable(),
managementRegistrationCustomizers.getIfAvailable(), heartbeatProperties,
sidecarProperties);
}
@Bean
public SidecarDiscoveryClient sidecarDiscoveryClient(
ConsulDiscoveryProperties properties, ConsulServiceRegistry serviceRegistry,
ConsulAutoRegistration registration) {
return new SidecarConsulDiscoveryClient(properties, serviceRegistry,
registration);
}
}

@ -0,0 +1,87 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.sidecar.consul;
import java.util.List;
import com.alibaba.cloud.sidecar.SidecarProperties;
import com.ecwid.consul.v1.agent.model.NewService;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.cloud.consul.discovery.ConsulDiscoveryProperties;
import org.springframework.cloud.consul.discovery.HeartbeatProperties;
import org.springframework.cloud.consul.serviceregistry.ConsulAutoRegistration;
import org.springframework.cloud.consul.serviceregistry.ConsulManagementRegistrationCustomizer;
import org.springframework.cloud.consul.serviceregistry.ConsulRegistrationCustomizer;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
/**
* @author www.itmuch.com
*/
public class SidecarConsulAutoRegistration extends ConsulAutoRegistration {
public SidecarConsulAutoRegistration(NewService service,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
ConsulDiscoveryProperties properties, ApplicationContext context,
HeartbeatProperties heartbeatProperties,
List<ConsulManagementRegistrationCustomizer> managementRegistrationCustomizers) {
super(service, autoServiceRegistrationProperties, properties, context,
heartbeatProperties, managementRegistrationCustomizers);
}
public static ConsulAutoRegistration registration(
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
ConsulDiscoveryProperties properties, ApplicationContext context,
List<ConsulRegistrationCustomizer> registrationCustomizers,
List<ConsulManagementRegistrationCustomizer> managementRegistrationCustomizers,
HeartbeatProperties heartbeatProperties,
SidecarProperties sidecarProperties) {
NewService service = new NewService();
String appName = getAppName(properties, context.getEnvironment());
service.setId(getInstanceId(sidecarProperties, context.getEnvironment()));
if (!properties.isPreferAgentAddress()) {
service.setAddress(sidecarProperties.getIp());
}
service.setName(normalizeForDns(appName));
service.setTags(createTags(properties));
// set health check, use alibaba sidecar self's port rather than polyglot app's
// port.
service.setPort(
Integer.valueOf(context.getEnvironment().getProperty("server.port")));
setCheck(service, autoServiceRegistrationProperties, properties, context,
heartbeatProperties);
service.setPort(sidecarProperties.getPort());
ConsulAutoRegistration registration = new ConsulAutoRegistration(service,
autoServiceRegistrationProperties, properties, context,
heartbeatProperties, managementRegistrationCustomizers);
customize(registrationCustomizers, registration);
return registration;
}
public static String getInstanceId(SidecarProperties sidecarProperties,
Environment environment) {
return String.format("%s-%s-%s",
environment.getProperty("spring.application.name"),
sidecarProperties.getIp(), sidecarProperties.getPort());
}
}

@ -0,0 +1,68 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.sidecar.consul;
import com.alibaba.cloud.sidecar.SidecarDiscoveryClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.consul.discovery.ConsulDiscoveryProperties;
import org.springframework.cloud.consul.serviceregistry.ConsulAutoRegistration;
import org.springframework.cloud.consul.serviceregistry.ConsulServiceRegistry;
/**
* @author www.itmuch.com
*/
public class SidecarConsulDiscoveryClient implements SidecarDiscoveryClient {
private static final Logger log = LoggerFactory
.getLogger(SidecarConsulDiscoveryClient.class);
private final ConsulDiscoveryProperties properties;
private final ConsulServiceRegistry serviceRegistry;
private final ConsulAutoRegistration registration;
public SidecarConsulDiscoveryClient(ConsulDiscoveryProperties properties,
ConsulServiceRegistry serviceRegistry, ConsulAutoRegistration registration) {
this.properties = properties;
this.serviceRegistry = serviceRegistry;
this.registration = registration;
}
@Override
public void registerInstance(String applicationName, String ip, Integer port) {
if (!this.properties.isRegister()) {
log.debug("Registration disabled.");
return;
}
serviceRegistry.register(registration);
}
@Override
public void deregisterInstance(String applicationName, String ip, Integer port) {
if (!this.properties.isRegister() || !this.properties.isDeregister()) {
return;
}
serviceRegistry.deregister(registration);
}
}

@ -0,0 +1,54 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.sidecar.nacos;
import com.alibaba.cloud.nacos.NacosDiscoveryAutoConfiguration;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.sidecar.SidecarAutoConfiguration;
import com.alibaba.cloud.sidecar.SidecarDiscoveryClient;
import com.alibaba.cloud.sidecar.SidecarProperties;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author www.itmuch.com
*/
@Configuration
@AutoConfigureBefore({ NacosDiscoveryAutoConfiguration.class,
SidecarAutoConfiguration.class })
@ConditionalOnClass(NacosDiscoveryProperties.class)
public class SidecarNacosAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SidecarNacosDiscoveryProperties sidecarNacosDiscoveryProperties(
SidecarProperties sidecarProperties) {
return new SidecarNacosDiscoveryProperties(sidecarProperties);
}
@Bean
@ConditionalOnMissingBean
public SidecarDiscoveryClient sidecarDiscoveryClient(
SidecarNacosDiscoveryProperties sidecarNacosDiscoveryProperties) {
return new SidecarNacosDiscoveryClient(sidecarNacosDiscoveryProperties);
}
}

@ -0,0 +1,61 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.sidecar.nacos;
import com.alibaba.cloud.sidecar.SidecarDiscoveryClient;
import com.alibaba.nacos.api.exception.NacosException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author www.itmuch.com
*/
public class SidecarNacosDiscoveryClient implements SidecarDiscoveryClient {
private static final Logger log = LoggerFactory
.getLogger(SidecarNacosDiscoveryClient.class);
private final SidecarNacosDiscoveryProperties sidecarNacosDiscoveryProperties;
public SidecarNacosDiscoveryClient(
SidecarNacosDiscoveryProperties sidecarNacosDiscoveryProperties) {
this.sidecarNacosDiscoveryProperties = sidecarNacosDiscoveryProperties;
}
@Override
public void registerInstance(String applicationName, String ip, Integer port) {
try {
this.sidecarNacosDiscoveryProperties.namingServiceInstance()
.registerInstance(applicationName, ip, port);
}
catch (NacosException e) {
log.warn("nacos exception happens", e);
}
}
@Override
public void deregisterInstance(String applicationName, String ip, Integer port) {
try {
this.sidecarNacosDiscoveryProperties.namingServiceInstance()
.deregisterInstance(applicationName, ip, port);
}
catch (NacosException e) {
log.warn("nacos exception happens", e);
}
}
}

@ -0,0 +1,49 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.sidecar.nacos;
import java.net.SocketException;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.sidecar.SidecarProperties;
import org.apache.commons.lang3.StringUtils;
/**
* @author itmuch.com
*/
public class SidecarNacosDiscoveryProperties extends NacosDiscoveryProperties {
private final SidecarProperties sidecarProperties;
public SidecarNacosDiscoveryProperties(SidecarProperties sidecarProperties) {
this.sidecarProperties = sidecarProperties;
}
@Override
public void init() throws SocketException {
super.init();
String ip = sidecarProperties.getIp();
if (StringUtils.isNotBlank(ip)) {
this.setIp(ip);
}
Integer port = sidecarProperties.getPort();
this.setPort(port);
}
}

@ -0,0 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.sidecar.nacos.SidecarNacosAutoConfiguration,\
com.alibaba.cloud.sidecar.SidecarAutoConfiguration,\
com.alibaba.cloud.sidecar.consul.SidecarConsulAutoConfiguration

@ -58,6 +58,12 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>

@ -16,18 +16,24 @@
package com.alibaba.alicloud.oss;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import com.alibaba.alicloud.oss.resource.OssStorageProtocolResolver;
import com.aliyun.oss.OSS;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.alibaba.alicloud.oss.resource.OssStorageProtocolResolver;
import com.aliyun.oss.OSS;
import static com.alibaba.alicloud.oss.OssConstants.OSS_TASK_EXECUTOR_BEAN_NAME;
/**
* OSS Auto {@link Configuration}
* OSS Auto {@link Configuration}.
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
@ -42,4 +48,12 @@ public class OssAutoConfiguration {
return new OssStorageProtocolResolver();
}
@Bean(name = OSS_TASK_EXECUTOR_BEAN_NAME)
@ConditionalOnMissingBean
public ExecutorService ossTaskExecutor() {
int coreSize = Runtime.getRuntime().availableProcessors();
return new ThreadPoolExecutor(coreSize, 128, 60, TimeUnit.SECONDS,
new SynchronousQueue<>());
}
}

@ -17,13 +17,29 @@
package com.alibaba.alicloud.oss;
/**
* OSS constants
* OSS constants.
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
public interface OssConstants {
public final class OssConstants {
String PREFIX = "spring.cloud.alibaba.oss";
String ENABLED = PREFIX + ".enabled";
/**
* Prefix of OSSConfigurationProperties.
*/
public static final String PREFIX = "spring.cloud.alibaba.oss";
/**
* Enable OSS.
*/
public static final String ENABLED = PREFIX + ".enabled";
/**
* OSS ThreadPool bean name.
*/
public static final String OSS_TASK_EXECUTOR_BEAN_NAME = "ossTaskExecutor";
private OssConstants() {
throw new AssertionError("Must not instantiate constant utility class");
}
}

@ -16,8 +16,10 @@
package com.alibaba.alicloud.oss.resource;
import com.aliyun.oss.OSS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@ -27,8 +29,6 @@ import org.springframework.core.io.ProtocolResolver;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import com.aliyun.oss.OSS;
/**
* A {@link ProtocolResolver} implementation for the {@code oss://} protocol.
*
@ -37,6 +37,9 @@ import com.aliyun.oss.OSS;
public class OssStorageProtocolResolver
implements ProtocolResolver, BeanFactoryPostProcessor, ResourceLoaderAware {
/**
* protocol of oss resource.
*/
public static final String PROTOCOL = "oss://";
private static final Logger log = LoggerFactory
@ -63,7 +66,7 @@ public class OssStorageProtocolResolver
if (!location.startsWith(PROTOCOL)) {
return null;
}
return new OssStorageResource(getOSS(), location);
return new OssStorageResource(getOSS(), location, beanFactory);
}
@Override
@ -82,4 +85,5 @@ public class OssStorageProtocolResolver
throws BeansException {
this.beanFactory = beanFactory;
}
}

@ -20,18 +20,28 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import java.util.concurrent.ExecutorService;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.Bucket;
import com.aliyun.oss.model.OSSObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.WritableResource;
import org.springframework.util.Assert;
import static com.alibaba.alicloud.oss.OssConstants.OSS_TASK_EXECUTOR_BEAN_NAME;
/**
* Implements {@link Resource} for reading and writing objects in Aliyun Object Storage
@ -43,18 +53,40 @@ import com.aliyun.oss.model.OSSObject;
* @see Bucket
* @see OSSObject
*/
public class OssStorageResource implements Resource {
public class OssStorageResource implements WritableResource {
private static final Logger logger = LoggerFactory
.getLogger(OssStorageResource.class);
private static final String MESSAGE_KEY_NOT_EXIST = "The specified key does not exist.";
private final OSS oss;
private final String bucketName;
private final String objectKey;
private final URI location;
public OssStorageResource(OSS oss, String location) {
private final boolean autoCreateFiles;
private final ExecutorService ossTaskExecutor;
private final ConfigurableListableBeanFactory beanFactory;
public OssStorageResource(OSS oss, String location,
ConfigurableListableBeanFactory beanFactory) {
this(oss, location, beanFactory, false);
}
public OssStorageResource(OSS oss, String location,
ConfigurableListableBeanFactory beanFactory, boolean autoCreateFiles) {
Assert.notNull(oss, "Object Storage Service can not be null");
Assert.isTrue(location.startsWith(OssStorageProtocolResolver.PROTOCOL),
"Location must start with " + OssStorageProtocolResolver.PROTOCOL);
this.oss = oss;
this.autoCreateFiles = autoCreateFiles;
this.beanFactory = beanFactory;
try {
URI locationUri = new URI(location);
this.bucketName = locationUri.getAuthority();
@ -70,6 +102,13 @@ public class OssStorageResource implements Resource {
catch (URISyntaxException e) {
throw new IllegalArgumentException("Invalid location: " + location, e);
}
this.ossTaskExecutor = this.beanFactory.getBean(OSS_TASK_EXECUTOR_BEAN_NAME,
ExecutorService.class);
}
public boolean isAutoCreateFiles() {
return this.autoCreateFiles;
}
@Override
@ -125,7 +164,7 @@ public class OssStorageResource implements Resource {
@Override
public Resource createRelative(String relativePath) throws IOException {
return new OssStorageResource(this.oss,
this.location.resolve(relativePath).toString());
this.location.resolve(relativePath).toString(), this.beanFactory);
}
@Override
@ -193,4 +232,70 @@ public class OssStorageResource implements Resource {
}
}
/**
* create a bucket.
* @return OSS Bucket
*/
public Bucket createBucket() {
return this.oss.createBucket(this.bucketName);
}
@Override
public boolean isWritable() {
return !isBucket() && (this.autoCreateFiles || exists());
}
/**
* acquire an OutputStream for write. Note: please close the stream after writing is
* done
* @return OutputStream of OSS resource
* @throws IOException throw by oss operation
*/
@Override
public OutputStream getOutputStream() throws IOException {
if (isBucket()) {
throw new IllegalStateException(
"Cannot open an output stream to a bucket: '" + getURI() + "'");
}
else {
OSSObject ossObject;
try {
ossObject = this.getOSSObject();
}
catch (OSSException ex) {
if (ex.getMessage() != null
&& ex.getMessage().startsWith(MESSAGE_KEY_NOT_EXIST)) {
ossObject = null;
}
else {
throw ex;
}
}
if (ossObject == null) {
if (!this.autoCreateFiles) {
throw new FileNotFoundException(
"The object was not found: " + getURI());
}
}
PipedInputStream in = new PipedInputStream();
final PipedOutputStream out = new PipedOutputStream(in);
ossTaskExecutor.submit(() -> {
try {
OssStorageResource.this.oss.putObject(bucketName, objectKey, in);
}
catch (Exception ex) {
logger.error("Failed to put object", ex);
}
});
return out;
}
}
}

@ -0,0 +1,104 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.alicloud.oss.resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.aliyun.oss.model.Bucket;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectResult;
import org.springframework.util.StreamUtils;
/**
* @author lich
*/
public class DummyOssClient {
private Map<String, byte[]> storeMap = new ConcurrentHashMap<>();
private Map<String, Bucket> bucketSet = new HashMap<>();
public String getStoreKey(String bucketName, String objectKey) {
return String.join(".", bucketName, objectKey);
}
public PutObjectResult putObject(String bucketName, String objectKey,
InputStream inputStream) {
try {
byte[] result = StreamUtils.copyToByteArray(inputStream);
storeMap.put(getStoreKey(bucketName, objectKey), result);
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
finally {
try {
inputStream.close();
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}
return new PutObjectResult();
}
public OSSObject getOSSObject(String bucketName, String objectKey) {
byte[] value = storeMap.get(this.getStoreKey(bucketName, objectKey));
if (value == null) {
return null;
}
OSSObject ossObject = new OSSObject();
ossObject.setBucketName(bucketName);
ossObject.setKey(objectKey);
InputStream inputStream = new ByteArrayInputStream(value);
ossObject.setObjectContent(inputStream);
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(value.length);
ossObject.setObjectMetadata(objectMetadata);
return ossObject;
}
public Bucket createBucket(String bucketName) {
if (bucketSet.containsKey(bucketName)) {
return bucketSet.get(bucketName);
}
Bucket bucket = new Bucket();
bucket.setCreationDate(new Date());
bucket.setName(bucketName);
bucketSet.put(bucketName, bucket);
return bucket;
}
public List<Bucket> bucketList() {
return new ArrayList<>(bucketSet.values());
}
}

@ -0,0 +1,273 @@
/*
* 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.alicloud.oss.resource;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import com.aliyun.oss.OSS;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.Resource;
import org.springframework.core.io.WritableResource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.StreamUtils;
import static com.alibaba.alicloud.oss.OssConstants.OSS_TASK_EXECUTOR_BEAN_NAME;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
/**
* @author lich
*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class OssStorageResourceTest {
/**
* Used to test exception messages and types.
*/
@Rule
public ExpectedException expectedEx = ExpectedException.none();
@Autowired
private ConfigurableListableBeanFactory beanFactory;
@Autowired
private OSS oss;
@Value("oss://aliyun-test-bucket/")
private Resource bucketResource;
@Value("oss://aliyun-test-bucket/myfilekey")
private Resource remoteResource;
public static byte[] generateRandomBytes(int blen) {
byte[] array = new byte[blen];
new Random().nextBytes(array);
return array;
}
@Test
public void testResourceType() {
assertThat(remoteResource.getClass()).isEqualTo(OssStorageResource.class);
OssStorageResource ossStorageResource = (OssStorageResource) remoteResource;
assertThat(ossStorageResource.getFilename()).isEqualTo("myfilekey");
assertThat(ossStorageResource.isBucket()).isEqualTo(false);
}
@Test
public void testValidObject() throws Exception {
assertThat(remoteResource.exists()).isEqualTo(true);
OssStorageResource ossStorageResource = (OssStorageResource) remoteResource;
assertThat(ossStorageResource.bucketExists()).isEqualTo(true);
assertThat(remoteResource.contentLength()).isEqualTo(4096L);
assertThat(remoteResource.getURI().toString())
.isEqualTo("oss://aliyun-test-bucket/myfilekey");
assertThat(remoteResource.getFilename()).isEqualTo("myfilekey");
}
@Test
public void testBucketResource() throws Exception {
assertThat(bucketResource.exists()).isEqualTo(true);
assertThat(((OssStorageResource) this.bucketResource).isBucket()).isEqualTo(true);
assertThat(((OssStorageResource) this.bucketResource).bucketExists())
.isEqualTo(true);
assertThat(bucketResource.getURI().toString())
.isEqualTo("oss://aliyun-test-bucket/");
assertThat(this.bucketResource.getFilename()).isEqualTo("aliyun-test-bucket");
}
@Test
public void testBucketNotEndingInSlash() {
assertThat(
new OssStorageResource(this.oss, "oss://aliyun-test-bucket", beanFactory)
.isBucket()).isEqualTo(true);
}
@Test
public void testSpecifyPathCorrect() {
OssStorageResource ossStorageResource = new OssStorageResource(this.oss,
"oss://aliyun-test-bucket/myfilekey", beanFactory, false);
assertThat(ossStorageResource.exists()).isEqualTo(true);
}
@Test
public void testSpecifyBucketCorrect() {
OssStorageResource ossStorageResource = new OssStorageResource(this.oss,
"oss://aliyun-test-bucket", beanFactory, false);
assertThat(ossStorageResource.isBucket()).isEqualTo(true);
assertThat(ossStorageResource.getBucket().getName())
.isEqualTo("aliyun-test-bucket");
assertThat(ossStorageResource.exists()).isEqualTo(true);
}
@Test
public void testBucketOutputStream() throws IOException {
this.expectedEx.expect(IllegalStateException.class);
this.expectedEx.expectMessage(
"Cannot open an output stream to a bucket: 'oss://aliyun-test-bucket/'");
((WritableResource) this.bucketResource).getOutputStream();
}
@Test
public void testBucketInputStream() throws IOException {
this.expectedEx.expect(IllegalStateException.class);
this.expectedEx.expectMessage(
"Cannot open an input stream to a bucket: 'oss://aliyun-test-bucket/'");
this.bucketResource.getInputStream();
}
@Test
public void testBucketContentLength() throws IOException {
this.expectedEx.expect(FileNotFoundException.class);
this.expectedEx.expectMessage("OSSObject not existed.");
this.bucketResource.contentLength();
}
@Test
public void testBucketFile() throws IOException {
this.expectedEx.expect(UnsupportedOperationException.class);
this.expectedEx.expectMessage(
"oss://aliyun-test-bucket/ cannot be resolved to absolute file path");
this.bucketResource.getFile();
}
@Test
public void testBucketLastModified() throws IOException {
this.expectedEx.expect(FileNotFoundException.class);
this.expectedEx.expectMessage("OSSObject not existed.");
this.bucketResource.lastModified();
}
@Test
public void testBucketResourceStatuses() {
assertThat(this.bucketResource.isOpen()).isEqualTo(false);
assertThat(((WritableResource) this.bucketResource).isWritable())
.isEqualTo(false);
assertThat(this.bucketResource.exists()).isEqualTo(true);
}
@Test
public void testWritable() throws Exception {
assertThat(this.remoteResource instanceof WritableResource).isEqualTo(true);
WritableResource writableResource = (WritableResource) this.remoteResource;
assertThat(writableResource.isWritable()).isEqualTo(true);
writableResource.getOutputStream();
}
@Test
public void testWritableOutputStream() throws Exception {
String location = "oss://aliyun-test-bucket/test";
OssStorageResource resource = new OssStorageResource(this.oss, location,
beanFactory, true);
OutputStream os = resource.getOutputStream();
assertThat(os).isNotNull();
byte[] randomBytes = generateRandomBytes(1203);
String expectedString = new String(randomBytes);
os.write(randomBytes);
os.close();
InputStream in = resource.getInputStream();
byte[] result = StreamUtils.copyToByteArray(in);
String actualString = new String(result);
assertThat(actualString).isEqualTo(expectedString);
}
@Test
public void testCreateBucket() {
String location = "oss://my-new-test-bucket/";
OssStorageResource resource = new OssStorageResource(this.oss, location,
beanFactory, true);
resource.createBucket();
assertThat(resource.bucketExists()).isEqualTo(true);
}
/**
* Configuration for the tests.
*/
@Configuration
@Import(OssStorageProtocolResolver.class)
static class TestConfiguration {
@Bean(name = OSS_TASK_EXECUTOR_BEAN_NAME)
@ConditionalOnMissingBean
public ExecutorService ossTaskExecutor() {
return new ThreadPoolExecutor(8, 128, 60, TimeUnit.SECONDS,
new SynchronousQueue<>());
}
@Bean
public static OSS mockOSS() {
DummyOssClient dummyOssStub = new DummyOssClient();
OSS oss = mock(OSS.class);
doAnswer(invocation -> dummyOssStub.putObject(invocation.getArgument(0),
invocation.getArgument(1), invocation.getArgument(2))).when(oss)
.putObject(Mockito.anyString(), Mockito.anyString(),
Mockito.any(InputStream.class));
doAnswer(invocation -> dummyOssStub.getOSSObject(invocation.getArgument(0),
invocation.getArgument(1))).when(oss).getObject(Mockito.anyString(),
Mockito.anyString());
doAnswer(invocation -> dummyOssStub.bucketList()).when(oss).listBuckets();
doAnswer(invocation -> dummyOssStub.createBucket(invocation.getArgument(0)))
.when(oss).createBucket(Mockito.anyString());
// prepare object
dummyOssStub.createBucket("aliyun-test-bucket");
byte[] content = generateRandomBytes(4096);
ByteArrayInputStream inputStream = new ByteArrayInputStream(content);
dummyOssStub.putObject("aliyun-test-bucket", "myfilekey", inputStream);
return oss;
}
}
}

@ -20,5 +20,6 @@
<module>spring-cloud-starter-stream-rocketmq</module>
<module>spring-cloud-starter-bus-rocketmq</module>
<module>spring-cloud-starter-dubbo</module>
<module>spring-cloud-starter-alibaba-sidecar</module>
</modules>
</project>

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-starter-alibaba-sidecar</artifactId>
<name>Spring Cloud Starter Alibaba Sidecar</name>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sidecar</artifactId>
</dependency>
</dependencies>
</project>

@ -22,6 +22,20 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.alibaba.cloud.stream.binder.rocketmq.consuming.RocketMQListenerBindingContainer;
import com.alibaba.cloud.stream.binder.rocketmq.integration.RocketMQInboundChannelAdapter;
import com.alibaba.cloud.stream.binder.rocketmq.integration.RocketMQMessageHandler;
import com.alibaba.cloud.stream.binder.rocketmq.integration.RocketMQMessageSource;
import com.alibaba.cloud.stream.binder.rocketmq.metrics.InstrumentationManager;
import com.alibaba.cloud.stream.binder.rocketmq.properties.RocketMQBinderConfigurationProperties;
import com.alibaba.cloud.stream.binder.rocketmq.properties.RocketMQConsumerProperties;
import com.alibaba.cloud.stream.binder.rocketmq.properties.RocketMQExtendedBindingProperties;
import com.alibaba.cloud.stream.binder.rocketmq.properties.RocketMQProducerProperties;
import com.alibaba.cloud.stream.binder.rocketmq.provisioning.RocketMQTopicProvisioner;
import com.alibaba.cloud.stream.binder.rocketmq.provisioning.selector.PartitionMessageQueueSelector;
import com.alibaba.cloud.stream.binder.rocketmq.support.JacksonRocketMQHeaderMapper;
import com.alibaba.cloud.stream.binder.rocketmq.support.RocketMQHeaderMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
@ -30,6 +44,7 @@ import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.spring.autoconfigure.RocketMQProperties;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.rocketmq.spring.support.RocketMQUtil;
import org.springframework.cloud.stream.binder.AbstractMessageChannelBinder;
import org.springframework.cloud.stream.binder.BinderSpecificPropertiesProvider;
import org.springframework.cloud.stream.binder.ExtendedConsumerProperties;
@ -48,22 +63,6 @@ import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.util.StringUtils;
import com.alibaba.cloud.stream.binder.rocketmq.consuming.RocketMQListenerBindingContainer;
import com.alibaba.cloud.stream.binder.rocketmq.integration.RocketMQInboundChannelAdapter;
import com.alibaba.cloud.stream.binder.rocketmq.integration.RocketMQMessageHandler;
import com.alibaba.cloud.stream.binder.rocketmq.integration.RocketMQMessageSource;
import com.alibaba.cloud.stream.binder.rocketmq.metrics.InstrumentationManager;
import com.alibaba.cloud.stream.binder.rocketmq.properties.RocketMQBinderConfigurationProperties;
import com.alibaba.cloud.stream.binder.rocketmq.properties.RocketMQConsumerProperties;
import com.alibaba.cloud.stream.binder.rocketmq.properties.RocketMQExtendedBindingProperties;
import com.alibaba.cloud.stream.binder.rocketmq.properties.RocketMQProducerProperties;
import com.alibaba.cloud.stream.binder.rocketmq.provisioning.RocketMQTopicProvisioner;
import com.alibaba.cloud.stream.binder.rocketmq.provisioning.selector.PartitionMessageQueueSelector;
import com.alibaba.cloud.stream.binder.rocketmq.support.JacksonRocketMQHeaderMapper;
import com.alibaba.cloud.stream.binder.rocketmq.support.RocketMQHeaderMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/

@ -59,7 +59,7 @@ import com.alibaba.cloud.stream.binder.rocketmq.properties.RocketMQConsumerPrope
import com.alibaba.cloud.stream.binder.rocketmq.support.RocketMQHeaderMapper;
/**
* A class that Listen on rocketmq message
* A class that Listen on rocketmq message.
* <p>
* this class will delegate {@link RocketMQListener} to handle message
*
@ -104,12 +104,16 @@ public class RocketMQListenerBindingContainer
private final ExtendedConsumerProperties<RocketMQConsumerProperties> rocketMQConsumerProperties;
private final RocketMQMessageChannelBinder rocketMQMessageChannelBinder;
private final RocketMQBinderConfigurationProperties rocketBinderConfigurationProperties;
// The following properties came from RocketMQConsumerProperties.
private ConsumeMode consumeMode;
private SelectorType selectorType;
private String selectorExpression;
private MessageModel messageModel;
public RocketMQListenerBindingContainer(
@ -120,8 +124,7 @@ public class RocketMQListenerBindingContainer
this.rocketBinderConfigurationProperties = rocketBinderConfigurationProperties;
this.rocketMQMessageChannelBinder = rocketMQMessageChannelBinder;
this.consumeMode = rocketMQConsumerProperties.getExtension().getOrderly()
? ConsumeMode.ORDERLY
: ConsumeMode.CONCURRENTLY;
? ConsumeMode.ORDERLY : ConsumeMode.CONCURRENTLY;
if (StringUtils.isEmpty(rocketMQConsumerProperties.getExtension().getSql())) {
this.selectorType = SelectorType.TAG;
this.selectorExpression = rocketMQConsumerProperties.getExtension().getTags();
@ -131,8 +134,7 @@ public class RocketMQListenerBindingContainer
this.selectorExpression = rocketMQConsumerProperties.getExtension().getSql();
}
this.messageModel = rocketMQConsumerProperties.getExtension().getBroadcasting()
? MessageModel.BROADCASTING
: MessageModel.CLUSTERING;
? MessageModel.BROADCASTING : MessageModel.CLUSTERING;
}
@Override
@ -386,6 +388,23 @@ public class RocketMQListenerBindingContainer
this.headerMapper = headerMapper;
}
/**
* Convert rocketmq {@link MessageExt} to Spring {@link Message}.
* @param messageExt the rocketmq message
* @return the converted Spring {@link Message}
*/
@SuppressWarnings("unchecked")
private Message convertToSpringMessage(MessageExt messageExt) {
// add reconsume-times header to messageExt
int reconsumeTimes = messageExt.getReconsumeTimes();
messageExt.putUserProperty(ROCKETMQ_RECONSUME_TIMES,
String.valueOf(reconsumeTimes));
Message message = RocketMQUtil.convertToSpringMessage(messageExt);
return MessageBuilder.fromMessage(message)
.copyHeaders(headerMapper.toHeaders(messageExt.getProperties())).build();
}
public class DefaultMessageListenerConcurrently
implements MessageListenerConcurrently {
@ -411,6 +430,7 @@ public class RocketMQListenerBindingContainer
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
}
public class DefaultMessageListenerOrderly implements MessageListenerOrderly {
@ -438,24 +458,7 @@ public class RocketMQListenerBindingContainer
return ConsumeOrderlyStatus.SUCCESS;
}
}
/**
* Convert rocketmq {@link MessageExt} to Spring {@link Message}
*
* @param messageExt the rocketmq message
* @return the converted Spring {@link Message}
*/
@SuppressWarnings("unchecked")
private Message convertToSpringMessage(MessageExt messageExt) {
// add reconsume-times header to messageExt
int reconsumeTimes = messageExt.getReconsumeTimes();
messageExt.putUserProperty(ROCKETMQ_RECONSUME_TIMES,
String.valueOf(reconsumeTimes));
Message message = RocketMQUtil.convertToSpringMessage(messageExt);
return MessageBuilder.fromMessage(message)
.copyHeaders(headerMapper.toHeaders(messageExt.getProperties())).build();
}
}

@ -16,21 +16,26 @@
package com.alibaba.cloud.stream.binder.rocketmq.properties;
import org.apache.rocketmq.common.MixAll;
import org.springframework.boot.context.properties.ConfigurationProperties;
import javax.validation.constraints.Pattern;
import com.alibaba.cloud.stream.binder.rocketmq.RocketMQBinderConstants;
import org.apache.rocketmq.common.MixAll;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
/**
* @author Timur Valiev
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
@ConfigurationProperties(prefix = "spring.cloud.stream.rocketmq.binder")
@Validated
public class RocketMQBinderConfigurationProperties {
/**
* The name server for rocketMQ, formats: `host:port;host:port`.
*/
@Pattern(regexp = "^[\\d.:;]+$", message = "nameServer needs to match expression \"host:port;host:port\"")
private String nameServer = RocketMQBinderConstants.DEFAULT_NAME_SERVER;
/**
@ -93,4 +98,5 @@ public class RocketMQBinderConfigurationProperties {
public void setCustomizedTraceTopic(String customizedTraceTopic) {
this.customizedTraceTopic = customizedTraceTopic;
}
}

@ -1,3 +1,19 @@
/*
* 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.stream.binder.rocketmq.provisioning.selector;
import java.util.List;
@ -11,7 +27,6 @@ import org.springframework.cloud.stream.binder.BinderHeaders;
/**
* @author wangxing
* @create 2019/7/3
*/
public class PartitionMessageQueueSelector implements MessageQueueSelector {

@ -20,23 +20,23 @@ import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.messaging.MessageHeaders;
import org.springframework.util.ClassUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
/**
* jackson header mapper for RocketMQ. Header types are added to a special header
* {@link #JSON_TYPES}.
@ -45,6 +45,7 @@ import com.google.common.collect.Maps;
* @since 2.1.1.RELEASE
*/
public class JacksonRocketMQHeaderMapper extends AbstractRocketMQHeaderMapper {
private final static Logger log = LoggerFactory
.getLogger(JacksonRocketMQHeaderMapper.class);
@ -71,8 +72,8 @@ public class JacksonRocketMQHeaderMapper extends AbstractRocketMQHeaderMapper {
@Override
public Map<String, String> fromHeaders(MessageHeaders headers) {
final Map<String, String> target = Maps.newHashMap();
final Map<String, String> jsonHeaders = Maps.newHashMap();
final Map<String, String> target = new HashMap<>();
final Map<String, String> jsonHeaders = new HashMap<>();
headers.forEach((key, value) -> {
if (matches(key)) {
if (value instanceof String) {
@ -104,7 +105,7 @@ public class JacksonRocketMQHeaderMapper extends AbstractRocketMQHeaderMapper {
@Override
public MessageHeaders toHeaders(Map<String, String> source) {
final Map<String, Object> target = Maps.newHashMap();
final Map<String, Object> target = new HashMap<>();
final Map<String, String> jsonTypes = decodeJsonTypes(source);
source.forEach((key, value) -> {
if (matches(key) && !(key.equals(JSON_TYPES))) {
@ -281,4 +282,5 @@ public class JacksonRocketMQHeaderMapper extends AbstractRocketMQHeaderMapper {
}
}
}

Loading…
Cancel
Save