diff --git a/pom.xml b/pom.xml index ae22e245..b49ad4f0 100644 --- a/pom.xml +++ b/pom.xml @@ -76,6 +76,7 @@ 2.1.3.RELEASE 2.1.3.RELEASE 2.1.3.RELEASE + 2.1.3.RELEASE 4.12 3.0 @@ -118,6 +119,7 @@ spring-cloud-alicloud-schedulerx spring-cloud-alicloud-sms spring-cloud-alibaba-coverage + spring-cloud-alibaba-sidecar @@ -187,6 +189,14 @@ import + + org.springframework.cloud + spring-cloud-consul-dependencies + ${spring-cloud-consul.version} + pom + import + + org.apache.dubbo dubbo-spring-boot-starter @@ -218,6 +228,7 @@ rocketmq-spring-boot-starter ${rocketmq.starter.version} + diff --git a/spring-cloud-alibaba-dependencies/pom.xml b/spring-cloud-alibaba-dependencies/pom.xml index 378bd455..5c8e7f03 100644 --- a/spring-cloud-alibaba-dependencies/pom.xml +++ b/spring-cloud-alibaba-dependencies/pom.xml @@ -20,8 +20,8 @@ 1.6.3 3.1.0 - 0.7.1 - 1.1.1 + 0.9.0 + 1.1.4 0.8.0 1.0.9 1.0.1 @@ -173,11 +173,6 @@ sentinel-apache-dubbo-adapter ${sentinel.version} - - com.alibaba.cloud - sentinel-dubbo-api - ${project.version} - com.alibaba.csp sentinel-cluster-server-default @@ -290,6 +285,11 @@ spring-cloud-alibaba-dubbo ${project.version} + + com.alibaba.cloud + spring-cloud-alibaba-sidecar + ${project.version} + @@ -356,6 +356,12 @@ ${project.version} + + com.alibaba.cloud + spring-cloud-starter-alibaba-sidecar + ${project.version} + + com.alibaba.cloud diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sidecar.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sidecar.adoc new file mode 100644 index 00000000..2a8832f5 --- /dev/null +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sidecar.adoc @@ -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] +---- + + com.alibaba.cloud + spring-cloud-starter-alibaba-sidecar + +---- +* 写配置: ++ +[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'); +}); +---- + +==== 测试 + +===== 测试1:Spring 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当网关在使用,经过了一层转发,性能有一定下降。 diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/spring-cloud-alibaba.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/spring-cloud-alibaba.adoc index c38e2134..e21f5501 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/spring-cloud-alibaba.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/spring-cloud-alibaba.adoc @@ -31,4 +31,4 @@ include::schedulerx.adoc[] include::sms.adoc[] - +include::sidecar.adoc[] diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java index 74bb1e38..b326085e 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java @@ -13,30 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.alibaba.cloud.dubbo.autoconfigure; -import java.util.*; - -import org.springframework.beans.factory.BeanClassLoaderAware; -import org.springframework.beans.factory.SmartInitializingSingleton; -import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; -import org.springframework.beans.factory.annotation.Autowired; -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.annotation.Configuration; -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.util.CollectionUtils; -import org.springframework.web.client.RestTemplate; +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; @@ -46,9 +30,33 @@ import com.alibaba.cloud.dubbo.metadata.resolver.DubboTransportedAttributesResol 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; +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; +import org.springframework.beans.factory.annotation.Autowired; +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.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; + /** * Dubbo Auto-{@link Configuration} for {@link LoadBalanced @LoadBalanced} - * {@link RestTemplate} + * {@link RestTemplate}. * * @author Mercy */ @@ -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 DUBBO_TRANSPORTED_CLASS = DubboTransported.class; @@ -89,19 +97,21 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration @Autowired(required = false) private Map 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,28 +119,31 @@ 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) { - DubboTransportedAttributesResolver attributesResolver = new DubboTransportedAttributesResolver( - environment); + if (event.getApplicationContext() == this.applicationContext) { - for (Map.Entry entry : restTemplates.entrySet()) { - String beanName = entry.getKey(); - Map dubboTranslatedAttributes = getDubboTranslatedAttributes( - beanName, attributesResolver); - if (!CollectionUtils.isEmpty(dubboTranslatedAttributes)) { - adaptRestTemplate(entry.getValue(), dubboTranslatedAttributes); + DubboTransportedAttributesResolver attributesResolver = new DubboTransportedAttributesResolver( + environment); + + for (Map.Entry entry : restTemplates.entrySet()) { + String beanName = entry.getKey(); + Map dubboTranslatedAttributes = getDubboTranslatedAttributes( + beanName, attributesResolver); + if (!CollectionUtils.isEmpty(dubboTranslatedAttributes)) { + adaptRestTemplate(entry.getValue(), dubboTranslatedAttributes); + } } } } /** * 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; + } + } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java index c5dda73f..0ebbbdf3 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java @@ -16,23 +16,12 @@ package com.alibaba.cloud.dubbo.autoconfigure; import java.util.Collection; +import java.util.Optional; import java.util.function.Supplier; -import org.apache.dubbo.config.ProtocolConfig; -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; -import org.springframework.boot.context.event.ApplicationFailedEvent; -import org.springframework.context.annotation.Bean; -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.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; @@ -42,6 +31,20 @@ 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; +import org.springframework.boot.context.event.ApplicationFailedEvent; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.util.CollectionUtils; /** * Spring Boot Auto-Configuration class for Dubbo Metadata @@ -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 dubboProtocolConfigSupplier( ObjectProvider> protocols) { diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboOpenFeignAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboOpenFeignAutoConfiguration.java index 0284aaa7..b3d71340 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboOpenFeignAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboOpenFeignAutoConfiguration.java @@ -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 diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceDiscoveryAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceDiscoveryAutoConfiguration.java index 0211a7a9..2a95f975 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceDiscoveryAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceDiscoveryAutoConfiguration.java @@ -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; diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/RequestMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/RequestMetadata.java index b5499cd0..612593da 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/RequestMetadata.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/RequestMetadata.java @@ -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 null diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/RestMethodMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/RestMethodMetadata.java index ee95a3fa..ace25205 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/RestMethodMetadata.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/RestMethodMetadata.java @@ -27,7 +27,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; /** - * Method Request Metadata + * Method Request Metadata. * * @author Mercy */ diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/ServiceRestMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/ServiceRestMetadata.java index 07771bf1..963edb09 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/ServiceRestMetadata.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/ServiceRestMetadata.java @@ -21,7 +21,7 @@ import java.util.Set; import com.fasterxml.jackson.annotation.JsonInclude; /** - * Service Rest Metadata + * Service Rest Metadata. * * @author Mercy * @see RestMethodMetadata diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/repository/DubboServiceMetadataRepository.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/repository/DubboServiceMetadataRepository.java index 032b1fae..bb8c5e79 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/repository/DubboServiceMetadataRepository.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/repository/DubboServiceMetadataRepository.java @@ -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 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 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 subscribedDubboMetadataServiceURLs = new LinkedMultiValueMap<>(); @@ -145,8 +147,10 @@ public class DubboServiceMetadataRepository // =================================== REST Metadata // ================================== // private volatile Set subscribedServices = emptySet(); + /** - * Key is application name Value is Map + * Key is application name Value is Map<RequestMetadata, + * DubboRestServiceMetadata>. */ private Map> 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 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 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 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> 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 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 null @@ -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; } + } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/repository/MetadataServiceInstanceSelector.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/repository/MetadataServiceInstanceSelector.java new file mode 100644 index 00000000..1ba6fb44 --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/repository/MetadataServiceInstanceSelector.java @@ -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 liuxx + */ +public interface MetadataServiceInstanceSelector { + + /** + * choose a service instance to get metadata. + * @param serviceInstances all service instance + * @return the service instance to get metadata + */ + Optional choose(List serviceInstances); + +} \ No newline at end of file diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/DubboServiceBeanMetadataResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/DubboServiceBeanMetadataResolver.java index a070a61a..fbee156a 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/DubboServiceBeanMetadataResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/DubboServiceBeanMetadataResolver.java @@ -59,7 +59,7 @@ public class DubboServiceBeanMetadataResolver private ClassLoader classLoader; /** - * Feign Contracts + * Feign Contracts. */ private Collection contracts; @@ -153,9 +153,8 @@ public class DubboServiceBeanMetadataResolver * Select feign contract methods *

* 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 selectFeignContractMethods(Class targetType) { diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/MetadataResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/MetadataResolver.java index 50af2633..03731383 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/MetadataResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/MetadataResolver.java @@ -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 Mercy */ 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 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 resolveMethodRestMetadata(Class targetType); + } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/AbstractSpringCloudRegistry.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/AbstractSpringCloudRegistry.java index 7b9df622..1299980a 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/AbstractSpringCloudRegistry.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/AbstractSpringCloudRegistry.java @@ -13,8 +13,36 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.alibaba.cloud.dubbo.registry; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.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 static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static org.apache.dubbo.common.URLBuilder.from; @@ -27,56 +55,31 @@ 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; -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; - -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; - /** * Abstract Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration - * abstraction, whose protocol is "spring-cloud" + * abstraction, whose protocol is "spring-cloud". * * @author Mercy */ 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 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()); } + } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/DelegatingRegistration.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/DelegatingRegistration.java index dcc63796..f08f7df9 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/DelegatingRegistration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/DelegatingRegistration.java @@ -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(); } + } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/SpringCloudRegistry.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/SpringCloudRegistry.java index f6a67f1d..fe732dab 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/SpringCloudRegistry.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/SpringCloudRegistry.java @@ -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 Mercy */ @@ -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); } + } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/SpringCloudRegistryFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/SpringCloudRegistryFactory.java index 15f44883..c3a9fe89 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/SpringCloudRegistryFactory.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/SpringCloudRegistryFactory.java @@ -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; - +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 Mercy * @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); } + } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/SubscribedServicesChangedEvent.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/SubscribedServicesChangedEvent.java index ea66aad4..9ad31d16 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/SubscribedServicesChangedEvent.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/SubscribedServicesChangedEvent.java @@ -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 diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboGenericServiceFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboGenericServiceFactory.java index 313abbef..2acffbd2 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboGenericServiceFactory.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboGenericServiceFactory.java @@ -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 Mercy */ @@ -56,7 +59,7 @@ public class DubboGenericServiceFactory { private final Logger logger = LoggerFactory.getLogger(getClass()); - private final ConcurrentMap> cache = new ConcurrentHashMap<>(); + private final ConcurrentMap> cache = new ConcurrentHashMap<>(); @Autowired private ObjectProvider> 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 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 removeGroups = new HashSet<>(cache.keySet()); + for (String key : removeGroups) { + if (key.contains(serviceName)) { + ReferenceBean referenceBean = cache.remove(key); + referenceBean.destroy(); + } + } } private void destroyReferenceBeans() { diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceInvocationHandler.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceInvocationHandler.java index bd374176..d9bc957e 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceInvocationHandler.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceInvocationHandler.java @@ -24,7 +24,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * {@link DubboMetadataService} {@link InvocationHandler} + * {@link DubboMetadataService} {@link InvocationHandler}. * * @author Mercy */ @@ -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]); } + } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceProxy.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceProxy.java index 55c0ee71..fbe283f2 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceProxy.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceProxy.java @@ -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 Mercy */ public class DubboMetadataServiceProxy implements BeanClassLoaderAware, DisposableBean { private final DubboGenericServiceFactory dubboGenericServiceFactory; + private final Map 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)); } + } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/DubboGenericServiceParameterResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/DubboGenericServiceParameterResolver.java index 870523c7..3805ee9e 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/DubboGenericServiceParameterResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/DubboGenericServiceParameterResolver.java @@ -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); diff --git a/spring-cloud-alibaba-examples/seata-example/account-service/src/main/resources/file.conf b/spring-cloud-alibaba-examples/seata-example/account-service/src/main/resources/file.conf index d686eeec..0777ae8a 100644 --- a/spring-cloud-alibaba-examples/seata-example/account-service/src/main/resources/file.conf +++ b/spring-cloud-alibaba-examples/seata-example/account-service/src/main/resources/file.conf @@ -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 + } } \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/seata-example/account-service/src/main/resources/registry.conf b/spring-cloud-alibaba-examples/seata-example/account-service/src/main/resources/registry.conf index 6dbeb22b..b98f5704 100644 --- a/spring-cloud-alibaba-examples/seata-example/account-service/src/main/resources/registry.conf +++ b/spring-cloud-alibaba-examples/seata-example/account-service/src/main/resources/registry.conf @@ -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" diff --git a/spring-cloud-alibaba-examples/seata-example/business-service/src/main/resources/file.conf b/spring-cloud-alibaba-examples/seata-example/business-service/src/main/resources/file.conf index b3047ec3..a6e431bc 100644 --- a/spring-cloud-alibaba-examples/seata-example/business-service/src/main/resources/file.conf +++ b/spring-cloud-alibaba-examples/seata-example/business-service/src/main/resources/file.conf @@ -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 + } } \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/seata-example/business-service/src/main/resources/registry.conf b/spring-cloud-alibaba-examples/seata-example/business-service/src/main/resources/registry.conf index 6dbeb22b..b98f5704 100644 --- a/spring-cloud-alibaba-examples/seata-example/business-service/src/main/resources/registry.conf +++ b/spring-cloud-alibaba-examples/seata-example/business-service/src/main/resources/registry.conf @@ -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" diff --git a/spring-cloud-alibaba-examples/seata-example/order-service/src/main/resources/file.conf b/spring-cloud-alibaba-examples/seata-example/order-service/src/main/resources/file.conf index 22184e04..89f55344 100644 --- a/spring-cloud-alibaba-examples/seata-example/order-service/src/main/resources/file.conf +++ b/spring-cloud-alibaba-examples/seata-example/order-service/src/main/resources/file.conf @@ -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 + } } \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/seata-example/order-service/src/main/resources/registry.conf b/spring-cloud-alibaba-examples/seata-example/order-service/src/main/resources/registry.conf index 6dbeb22b..b98f5704 100644 --- a/spring-cloud-alibaba-examples/seata-example/order-service/src/main/resources/registry.conf +++ b/spring-cloud-alibaba-examples/seata-example/order-service/src/main/resources/registry.conf @@ -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" diff --git a/spring-cloud-alibaba-examples/seata-example/readme-zh.md b/spring-cloud-alibaba-examples/seata-example/readme-zh.md index cc18b559..2723de58 100644 --- a/spring-cloud-alibaba-examples/seata-example/readme-zh.md +++ b/spring-cloud-alibaba-examples/seata-example/readme-zh.md @@ -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 diff --git a/spring-cloud-alibaba-examples/seata-example/storage-service/src/main/resources/file.conf b/spring-cloud-alibaba-examples/seata-example/storage-service/src/main/resources/file.conf index 930a65c0..2915b99c 100644 --- a/spring-cloud-alibaba-examples/seata-example/storage-service/src/main/resources/file.conf +++ b/spring-cloud-alibaba-examples/seata-example/storage-service/src/main/resources/file.conf @@ -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 + } } \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/seata-example/storage-service/src/main/resources/registry.conf b/spring-cloud-alibaba-examples/seata-example/storage-service/src/main/resources/registry.conf index 6dbeb22b..b98f5704 100644 --- a/spring-cloud-alibaba-examples/seata-example/storage-service/src/main/resources/registry.conf +++ b/spring-cloud-alibaba-examples/seata-example/storage-service/src/main/resources/registry.conf @@ -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" diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml index f0b9a131..02dc352f 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml @@ -25,6 +25,7 @@ com.alibaba.cloud sentinel-dubbo-api + ${project.version} diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example/pom.xml index cd888ac8..72f91ebd 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example/pom.xml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example/pom.xml @@ -25,6 +25,7 @@ com.alibaba.cloud sentinel-dubbo-api + ${project.version} diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigManager.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigManager.java index 4b5e98f9..367f0b2f 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigManager.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigManager.java @@ -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 liaochuntao */ -public class NacosConfigManager implements ApplicationContextAware { +public class NacosConfigManager { - private ConfigService configService; + private static ConfigService service = null; + + @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); + } + } + return service; } - @Override - public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { - NacosConfigProperties properties = applicationContext - .getBean(NacosConfigProperties.class); - configService = properties.configServiceInstance(); - } } diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigProperties.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigProperties.java index d03c399f..411fb146 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigProperties.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigProperties.java @@ -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; - } - } } diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySourceLocator.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySourceLocator.java index 10a48cd8..d55b5dfe 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySourceLocator.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySourceLocator.java @@ -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 (NacosContextRefresher.getRefreshCount() != 0) { - NacosPropertySource ps; - if (!isRefreshable) { - ps = NacosPropertySourceRepository.getNacosPropertySource(dataId); - } - else { - ps = nacosPropertySourceBuilder.build(dataId, group, fileExtension, true); - } + 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); + } - composite.addFirstPropertySource(ps); + private NacosPropertySource loadNacosPropertySource(final String dataId, + final String group, String fileExtension, boolean isRefreshable) { + if (NacosContextRefresher.getRefreshCount() != 0) { + if (!isRefreshable) { + return NacosPropertySourceRepository.getNacosPropertySource(dataId); + } } - else { - NacosPropertySource ps = nacosPropertySourceBuilder.build(dataId, group, - fileExtension, isRefreshable); - composite.addFirstPropertySource(ps); + return nacosPropertySourceBuilder.build(dataId, group, fileExtension, + isRefreshable); + } + + /** + * 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; } + if (ignoreEmpty && nacosPropertySource.getSource().isEmpty()) { + return; + } + composite.addFirstPropertySource(nacosPropertySource); } private static void checkDataIdFileExtension(String[] dataIdArray) { diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/diagnostics/analyzer/NacosConnectionFailureAnalyzer.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/diagnostics/analyzer/NacosConnectionFailureAnalyzer.java index b8109b89..3c7ae0e2 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/diagnostics/analyzer/NacosConnectionFailureAnalyzer.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/diagnostics/analyzer/NacosConnectionFailureAnalyzer.java @@ -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); } + } diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/diagnostics/analyzer/NacosConnectionFailureException.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/diagnostics/analyzer/NacosConnectionFailureException.java index f01458d4..2e9efd94 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/diagnostics/analyzer/NacosConnectionFailureException.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/diagnostics/analyzer/NacosConnectionFailureException.java @@ -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; + this.serverAddr = serverAddr; } - String getDomain() { - return domain; - } - - String getPort() { - return port; + public String getServerAddr() { + return serverAddr; } } diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/endpoint/NacosConfigEndpoint.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/endpoint/NacosConfigEndpoint.java index 2594ae92..5ebe1ab2 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/endpoint/NacosConfigEndpoint.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/endpoint/NacosConfigEndpoint.java @@ -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") diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/AbstractNacosDataParser.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/AbstractNacosDataParser.java index 42f49bbb..626db91f 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/AbstractNacosDataParser.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/AbstractNacosDataParser.java @@ -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 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 reloadMap(Map map) { if (map == null || map.isEmpty()) { diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosDataJsonParser.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosDataJsonParser.java index 8e500e33..a4fbe327 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosDataJsonParser.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosDataJsonParser.java @@ -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 parseJSON2Map(String json) throws IOException { Map map = new HashMap<>(32); diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosDataParserHandler.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosDataParserHandler.java index e3168c91..e40f7f24 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosDataParserHandler.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosDataParserHandler.java @@ -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(); + } } diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosDataXmlParser.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosDataXmlParser.java index e9431ba4..f9e0173c 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosDataXmlParser.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/parser/NacosDataXmlParser.java @@ -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 parseXml2Map(String xml) throws IOException { - xml = xml.replaceAll("\\r", "") - .replaceAll("\\n", "") - .replaceAll("\\t", ""); + xml = xml.replaceAll("\\r", "").replaceAll("\\n", "").replaceAll("\\t", ""); Map map = new HashMap<>(32); try { DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance() diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java index 85e63304..2d84f8a1 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java @@ -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); } diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosNamingManager.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosNamingManager.java index 9bf40a45..0ac6c5dd 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosNamingManager.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosNamingManager.java @@ -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 liaochuntao */ -public class NacosNamingManager implements ApplicationContextAware { +public class NacosNamingManager { - private NamingService namingService; - private NamingMaintainService namingMaintainService; + private static NamingService namingService = null; + + private static NamingMaintainService namingMaintainService = null; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; public NamingService getNamingService() { + if (Objects.isNull(namingService)) { + namingService = discoveryProperties.namingServiceInstance(); + } return namingService; } public NamingMaintainService getNamingMaintainService() { + if (Objects.isNull(namingMaintainService)) { + namingMaintainService = discoveryProperties.namingMaintainServiceInstance(); + } return namingMaintainService; } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { - NacosDiscoveryProperties properties = applicationContext - .getBean(NacosDiscoveryProperties.class); - namingService = properties.namingServiceInstance(); - namingMaintainService = properties.namingMaintainServiceInstance(); - } } diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/registry/NacosServiceRegistry.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/registry/NacosServiceRegistry.java index 9e8406bc..2e27a158 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/registry/NacosServiceRegistry.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/registry/NacosServiceRegistry.java @@ -64,7 +64,7 @@ public class NacosServiceRegistry implements ServiceRegistry { 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) { diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/ribbon/ExtendBalancer.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/ribbon/ExtendBalancer.java index 06111263..48424f74 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/ribbon/ExtendBalancer.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/ribbon/ExtendBalancer.java @@ -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; diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/ribbon/NacosRule.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/ribbon/NacosRule.java index f0c5fcda..43382485 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/ribbon/NacosRule.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/ribbon/NacosRule.java @@ -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; diff --git a/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/hystrix/SeataHystrixConcurrencyStrategy.java b/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/hystrix/SeataHystrixConcurrencyStrategy.java index 0bff8173..dcb71f90 100644 --- a/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/hystrix/SeataHystrixConcurrencyStrategy.java +++ b/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/hystrix/SeataHystrixConcurrencyStrategy.java @@ -58,6 +58,7 @@ public class SeataHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy private static class SeataContextCallable implements Callable { private final Callable actual; + private final String xid; SeataContextCallable(Callable actual) { diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/RuleType.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/RuleType.java index 4a376fd2..5af37321 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/RuleType.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/RuleType.java @@ -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; diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/SentinelDataSourceConstants.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/SentinelDataSourceConstants.java deleted file mode 100644 index 5240ba26..00000000 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/SentinelDataSourceConstants.java +++ /dev/null @@ -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 Jim - */ -public interface SentinelDataSourceConstants { - - String PROPERTY_PREFIX = "spring.cloud.sentinel"; - -} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/AbstractDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/AbstractDataSourceProperties.java index df89dbd9..e9867914 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/AbstractDataSourceProperties.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/AbstractDataSourceProperties.java @@ -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; diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/ApolloDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/ApolloDataSourceProperties.java index d67baf04..6c392258 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/ApolloDataSourceProperties.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/ApolloDataSourceProperties.java @@ -30,8 +30,10 @@ public class ApolloDataSourceProperties extends AbstractDataSourceProperties { @NotEmpty private String namespaceName; + @NotEmpty private String flowRulesKey; + private String defaultFlowRuleValue; public ApolloDataSourceProperties() { diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/DataSourcePropertiesConfiguration.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/DataSourcePropertiesConfiguration.java index 9726668f..0492e57d 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/DataSourcePropertiesConfiguration.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/DataSourcePropertiesConfiguration.java @@ -66,11 +66,11 @@ public class DataSourcePropertiesConfiguration { this.apollo = apollo; } - public DataSourcePropertiesConfiguration(RedisDataSourceProperties redis) { - this.redis = redis; - } + public DataSourcePropertiesConfiguration(RedisDataSourceProperties redis) { + this.redis = redis; + } - public FileDataSourceProperties getFile() { + public FileDataSourceProperties getFile() { return file; } diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/FileDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/FileDataSourceProperties.java index c8551c24..e235045a 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/FileDataSourceProperties.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/FileDataSourceProperties.java @@ -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() { diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/NacosDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/NacosDataSourceProperties.java index f8673d0b..eaac832c 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/NacosDataSourceProperties.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/NacosDataSourceProperties.java @@ -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() { diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/RedisDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/RedisDataSourceProperties.java index 8bea7625..08f3bc20 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/RedisDataSourceProperties.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/RedisDataSourceProperties.java @@ -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} @@ -31,143 +31,143 @@ import java.util.List; */ public class RedisDataSourceProperties extends AbstractDataSourceProperties { - public RedisDataSourceProperties() { - super(RedisDataSourceFactoryBean.class.getName()); - } + public RedisDataSourceProperties() { + super(RedisDataSourceFactoryBean.class.getName()); + } - /** - * redis server host - */ - private String host = "localhost"; + /** + * redis server host. + */ + private String host = "localhost"; - /** - * redis server port - */ - private int port = 6379; + /** + * redis server port. + */ + private int port = 6379; - /** - * redis server password - */ - private String password; + /** + * redis server password. + */ + private String password; - /** - * redis server default select database - */ - private int database; + /** + * redis server default select database. + */ + private int database; - /** - * redis server timeout - */ - private Duration timeout; + /** + * redis server timeout. + */ + private Duration timeout; - /** - * Comma-separated list of "host:port" pairs. - */ - private List nodes; + /** + * Comma-separated list of "host:port" pairs. + */ + private List nodes; - /** - * data key in Redis - */ - private String ruleKey; + /** + * data key in Redis. + */ + private String ruleKey; - /** - * channel to subscribe in Redis - */ - private String channel; + /** + * channel to subscribe in Redis. + */ + private String channel; - /** - * redis sentinel model - */ - private String masterId; + /** + * redis sentinel model. + */ + private String masterId; - @Override - public void preCheck(String dataSourceName) { - super.preCheck(dataSourceName); - if (StringUtils.isEmpty(ruleKey)) { - throw new IllegalArgumentException( - "RedisDataSource ruleKey can not be empty"); - } + @Override + public void preCheck(String dataSourceName) { + super.preCheck(dataSourceName); + if (StringUtils.isEmpty(ruleKey)) { + throw new IllegalArgumentException( + "RedisDataSource ruleKey can not be empty"); + } - if (StringUtils.isEmpty(channel)) { - throw new IllegalArgumentException( - "RedisDataSource channel can not be empty"); - } + if (StringUtils.isEmpty(channel)) { + throw new IllegalArgumentException( + "RedisDataSource channel can not be empty"); + } - if (!StringUtils.isEmpty(masterId) && StringUtils.isEmpty(masterId)) { - throw new IllegalArgumentException( - "RedisDataSource sentinel model,masterId can not be empty"); - } - } + if (!StringUtils.isEmpty(masterId) && StringUtils.isEmpty(masterId)) { + throw new IllegalArgumentException( + "RedisDataSource sentinel model,masterId can not be empty"); + } + } - public String getHost() { - return host; - } + public String getHost() { + return host; + } - public void setHost(String host) { - this.host = host; - } + public void setHost(String host) { + this.host = host; + } - public int getPort() { - return port; - } + public int getPort() { + return port; + } - public void setPort(int port) { - this.port = port; - } + public void setPort(int port) { + this.port = port; + } - public String getRuleKey() { - return ruleKey; - } + public String getRuleKey() { + return ruleKey; + } - public void setRuleKey(String ruleKey) { - this.ruleKey = ruleKey; - } + public void setRuleKey(String ruleKey) { + this.ruleKey = ruleKey; + } - public String getChannel() { - return channel; - } + public String getChannel() { + return channel; + } - public void setChannel(String channel) { - this.channel = channel; - } + public void setChannel(String channel) { + this.channel = channel; + } - public String getPassword() { - return password; - } + public String getPassword() { + return password; + } - public void setPassword(String password) { - this.password = password; - } + public void setPassword(String password) { + this.password = password; + } - public int getDatabase() { - return database; - } + public int getDatabase() { + return database; + } - public void setDatabase(int database) { - this.database = database; - } + public void setDatabase(int database) { + this.database = database; + } - public Duration getTimeout() { - return timeout; - } + public Duration getTimeout() { + return timeout; + } - public void setTimeout(Duration timeout) { - this.timeout = timeout; - } + public void setTimeout(Duration timeout) { + this.timeout = timeout; + } - public List getNodes() { - return nodes; - } + public List getNodes() { + return nodes; + } - public void setNodes(List nodes) { - this.nodes = nodes; - } + public void setNodes(List nodes) { + this.nodes = nodes; + } - public String getMasterId() { - return masterId; - } + public String getMasterId() { + return masterId; + } - public void setMasterId(String masterId) { - this.masterId = masterId; - } + public void setMasterId(String masterId) { + this.masterId = masterId; + } } diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/ApolloDataSourceFactoryBean.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/ApolloDataSourceFactoryBean.java index 47843e5c..4377afd4 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/ApolloDataSourceFactoryBean.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/ApolloDataSourceFactoryBean.java @@ -30,8 +30,11 @@ import com.alibaba.csp.sentinel.datasource.apollo.ApolloDataSource; public class ApolloDataSourceFactoryBean implements FactoryBean { private String namespaceName; + private String flowRulesKey; + private String defaultFlowRuleValue; + private Converter converter; @Override diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/FileRefreshableDataSourceFactoryBean.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/FileRefreshableDataSourceFactoryBean.java index 0042134b..8e439997 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/FileRefreshableDataSourceFactoryBean.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/FileRefreshableDataSourceFactoryBean.java @@ -34,9 +34,13 @@ public class FileRefreshableDataSourceFactoryBean implements FactoryBean { private String file; + private String charset; + private long recommendRefreshMs; + private int bufSize; + private Converter converter; @Override diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/NacosDataSourceFactoryBean.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/NacosDataSourceFactoryBean.java index da640f8e..00faaea6 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/NacosDataSourceFactoryBean.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/NacosDataSourceFactoryBean.java @@ -34,13 +34,19 @@ import com.alibaba.nacos.api.PropertyKeyConst; public class NacosDataSourceFactoryBean implements FactoryBean { 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 diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/RedisDataSourceFactoryBean.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/RedisDataSourceFactoryBean.java index bbf8435b..4e36e17d 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/RedisDataSourceFactoryBean.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/RedisDataSourceFactoryBean.java @@ -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. * @@ -35,152 +36,154 @@ import java.util.List; */ public class RedisDataSourceFactoryBean implements FactoryBean { - private String host; + private String host; - private int port; + private int port; - private int database; + private int database; - private Duration timeout; + private Duration timeout; - /** - * Comma-separated list of "host:port" pairs. - */ - private List nodes; + /** + * Comma-separated list of "host:port" pairs. + */ + private List nodes; - private Converter converter; + private Converter converter; - /** - * data key in Redis - */ - private String ruleKey; + /** + * data key in Redis. + */ + private String ruleKey; - /** - * channel to subscribe in Redis - */ - private String channel; + /** + * channel to subscribe in Redis. + */ + private String channel; - /** - * redis server password - */ - private String password; + /** + * redis server password. + */ + private String password; - private String masterId; + private String masterId; - @Override - public RedisDataSource getObject() { - RedisConnectionConfig.Builder builder = RedisConnectionConfig.builder(); + @Override + public RedisDataSource getObject() { + RedisConnectionConfig.Builder builder = RedisConnectionConfig.builder(); - if (nodes == null || nodes.isEmpty()) { - 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); - } - }); - builder.withSentinelMasterId(masterId); - } + if (nodes == null || nodes.isEmpty()) { + 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); + } + }); + builder.withSentinelMasterId(masterId); + } - if (timeout != null) { - builder.withTimeout(timeout.toMillis()); - } + if (timeout != null) { + builder.withTimeout(timeout.toMillis()); + } - if (StringUtils.hasText(password)) { - builder.withPassword(password); - } + if (StringUtils.hasText(password)) { + builder.withPassword(password); + } - return new RedisDataSource>(builder.build(), ruleKey, channel, converter); - } + return new RedisDataSource>(builder.build(), ruleKey, channel, + converter); + } - @Override - public Class getObjectType() { - return RedisDataSource.class; - } + @Override + public Class getObjectType() { + return RedisDataSource.class; + } - public Converter getConverter() { - return converter; - } + public Converter getConverter() { + return converter; + } - public void setConverter(Converter converter) { - this.converter = converter; - } + public void setConverter(Converter converter) { + this.converter = converter; + } - public String getHost() { - return host; - } + public String getHost() { + return host; + } - public void setHost(String host) { - this.host = host; - } + public void setHost(String host) { + this.host = host; + } - public int getPort() { - return port; - } + public int getPort() { + return port; + } - public void setPort(int port) { - this.port = port; - } + public void setPort(int port) { + this.port = port; + } - public String getRuleKey() { - return ruleKey; - } + public String getRuleKey() { + return ruleKey; + } - public void setRuleKey(String ruleKey) { - this.ruleKey = ruleKey; - } + public void setRuleKey(String ruleKey) { + this.ruleKey = ruleKey; + } - public String getChannel() { - return channel; - } + public String getChannel() { + return channel; + } - public void setChannel(String channel) { - this.channel = channel; - } + public void setChannel(String channel) { + this.channel = channel; + } - public String getPassword() { - return password; - } + public String getPassword() { + return password; + } - public void setPassword(String password) { - this.password = password; - } + public void setPassword(String password) { + this.password = password; + } - public int getDatabase() { - return database; - } + public int getDatabase() { + return database; + } - public void setDatabase(int database) { - this.database = database; - } + public void setDatabase(int database) { + this.database = database; + } - public Duration getTimeout() { - return timeout; - } + public Duration getTimeout() { + return timeout; + } - public void setTimeout(Duration timeout) { - this.timeout = timeout; - } + public void setTimeout(Duration timeout) { + this.timeout = timeout; + } - public List getNodes() { - return nodes; - } + public List getNodes() { + return nodes; + } - public void setNodes(List nodes) { - this.nodes = nodes; - } + public void setNodes(List nodes) { + this.nodes = nodes; + } - public String getMasterId() { - return masterId; - } + public String getMasterId() { + return masterId; + } - public void setMasterId(String masterId) { - this.masterId = masterId; - } + public void setMasterId(String masterId) { + this.masterId = masterId; + } } diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/ZookeeperDataSourceFactoryBean.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/ZookeeperDataSourceFactoryBean.java index 5731d8f1..09c339f1 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/ZookeeperDataSourceFactoryBean.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/ZookeeperDataSourceFactoryBean.java @@ -35,6 +35,7 @@ public class ZookeeperDataSourceFactoryBean implements FactoryBean map = new HashMap(); + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, + SpringApplication springApplication) { + addDefaultPropertySource(environment); + } - configureDefaultProperties(map); + private void addDefaultPropertySource(ConfigurableEnvironment environment) { - addOrReplace(environment.getPropertySources(), map); - } + Map map = new HashMap(); - private void configureDefaultProperties(Map source) { - // Required Properties - source.put(SENTINEL_FILTER_ENABLED, "false"); - } + configureDefaultProperties(map); - private void addOrReplace(MutablePropertySources propertySources, - Map map) { - MapPropertySource target = null; - if (propertySources.contains(PROPERTY_SOURCE_NAME)) { - PropertySource source = propertySources.get(PROPERTY_SOURCE_NAME); - if (source instanceof MapPropertySource) { - target = (MapPropertySource) source; - for (String key : map.keySet()) { - if (!target.containsProperty(key)) { - target.getSource().put(key, map.get(key)); - } - } - } - } - if (target == null) { - target = new MapPropertySource(PROPERTY_SOURCE_NAME, map); - } - if (!propertySources.contains(PROPERTY_SOURCE_NAME)) { - propertySources.addLast(target); - } - } + addOrReplace(environment.getPropertySources(), map); + } + private void configureDefaultProperties(Map source) { + // Required Properties + source.put(SENTINEL_FILTER_ENABLED, "false"); + } + + private void addOrReplace(MutablePropertySources propertySources, + Map map) { + MapPropertySource target = null; + if (propertySources.contains(PROPERTY_SOURCE_NAME)) { + PropertySource source = propertySources.get(PROPERTY_SOURCE_NAME); + if (source instanceof MapPropertySource) { + target = (MapPropertySource) source; + for (String key : map.keySet()) { + if (!target.containsProperty(key)) { + target.getSource().put(key, map.get(key)); + } + } + } + } + if (target == null) { + target = new MapPropertySource(PROPERTY_SOURCE_NAME, map); + } + if (!propertySources.contains(PROPERTY_SOURCE_NAME)) { + propertySources.addLast(target); + } + } } diff --git a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelCircuitBreakerConfiguration.java b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelCircuitBreakerConfiguration.java index 526719bc..7642d9e8 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelCircuitBreakerConfiguration.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelCircuitBreakerConfiguration.java @@ -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 { + } diff --git a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelDataSourceHandler.java b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelDataSourceHandler.java index 6c7d3f1d..54ad845d 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelDataSourceHandler.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/custom/SentinelDataSourceHandler.java @@ -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; diff --git a/spring-cloud-alibaba-sidecar/pom.xml b/spring-cloud-alibaba-sidecar/pom.xml new file mode 100644 index 00000000..d4d7898d --- /dev/null +++ b/spring-cloud-alibaba-sidecar/pom.xml @@ -0,0 +1,43 @@ + + + + com.alibaba.cloud + spring-cloud-alibaba + 2.1.1.BUILD-SNAPSHOT + + 4.0.0 + + spring-cloud-alibaba-sidecar + Spring Cloud Alibaba Sidecar + An easy way to integrate polyglot apps for Spring Cloud Alibaba. + + + + org.springframework.cloud + spring-cloud-starter-gateway + + + + org.springframework.boot + spring-boot-starter-actuator + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.cloud + spring-cloud-starter-consul-discovery + true + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + diff --git a/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/SidecarAutoConfiguration.java b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/SidecarAutoConfiguration.java new file mode 100644 index 00000000..d31e5320 --- /dev/null +++ b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/SidecarAutoConfiguration.java @@ -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; + } + +} diff --git a/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/SidecarDiscoveryClient.java b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/SidecarDiscoveryClient.java new file mode 100644 index 00000000..8a67c575 --- /dev/null +++ b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/SidecarDiscoveryClient.java @@ -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); + +} diff --git a/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/SidecarHealthChecker.java b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/SidecarHealthChecker.java new file mode 100644 index 00000000..2976e875 --- /dev/null +++ b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/SidecarHealthChecker.java @@ -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); + } + +} diff --git a/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/SidecarHealthIndicator.java b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/SidecarHealthIndicator.java new file mode 100644 index 00000000..4d07a380 --- /dev/null +++ b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/SidecarHealthIndicator.java @@ -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> exchange = this.restTemplate.exchange(uri, + HttpMethod.GET, null, + new ParameterizedTypeReference>() { + }); + + Map 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"); + } + +} diff --git a/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/SidecarProperties.java b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/SidecarProperties.java new file mode 100644 index 00000000..b95f9189 --- /dev/null +++ b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/SidecarProperties.java @@ -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; + } + +} diff --git a/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/consul/SidecarConsulAutoConfiguration.java b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/consul/SidecarConsulAutoConfiguration.java new file mode 100644 index 00000000..d3f40929 --- /dev/null +++ b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/consul/SidecarConsulAutoConfiguration.java @@ -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> registrationCustomizers, + ObjectProvider> 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); + } + +} diff --git a/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/consul/SidecarConsulAutoRegistration.java b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/consul/SidecarConsulAutoRegistration.java new file mode 100644 index 00000000..f53a0a1d --- /dev/null +++ b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/consul/SidecarConsulAutoRegistration.java @@ -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 managementRegistrationCustomizers) { + super(service, autoServiceRegistrationProperties, properties, context, + heartbeatProperties, managementRegistrationCustomizers); + } + + public static ConsulAutoRegistration registration( + AutoServiceRegistrationProperties autoServiceRegistrationProperties, + ConsulDiscoveryProperties properties, ApplicationContext context, + List registrationCustomizers, + List 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()); + } + +} diff --git a/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/consul/SidecarConsulDiscoveryClient.java b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/consul/SidecarConsulDiscoveryClient.java new file mode 100644 index 00000000..49088f3a --- /dev/null +++ b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/consul/SidecarConsulDiscoveryClient.java @@ -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); + } + +} diff --git a/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/nacos/SidecarNacosAutoConfiguration.java b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/nacos/SidecarNacosAutoConfiguration.java new file mode 100644 index 00000000..1c1f28af --- /dev/null +++ b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/nacos/SidecarNacosAutoConfiguration.java @@ -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); + } + +} diff --git a/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/nacos/SidecarNacosDiscoveryClient.java b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/nacos/SidecarNacosDiscoveryClient.java new file mode 100644 index 00000000..e4302afe --- /dev/null +++ b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/nacos/SidecarNacosDiscoveryClient.java @@ -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); + } + } + +} diff --git a/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/nacos/SidecarNacosDiscoveryProperties.java b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/nacos/SidecarNacosDiscoveryProperties.java new file mode 100644 index 00000000..d58c60c1 --- /dev/null +++ b/spring-cloud-alibaba-sidecar/src/main/java/com/alibaba/cloud/sidecar/nacos/SidecarNacosDiscoveryProperties.java @@ -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); + } + +} diff --git a/spring-cloud-alibaba-sidecar/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-sidecar/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..726e9382 --- /dev/null +++ b/spring-cloud-alibaba-sidecar/src/main/resources/META-INF/spring.factories @@ -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 \ No newline at end of file diff --git a/spring-cloud-alicloud-oss/pom.xml b/spring-cloud-alicloud-oss/pom.xml index 85318849..30664e8a 100644 --- a/spring-cloud-alicloud-oss/pom.xml +++ b/spring-cloud-alicloud-oss/pom.xml @@ -58,6 +58,12 @@ test + + org.mockito + mockito-core + test + + diff --git a/spring-cloud-alicloud-oss/src/main/java/com/alibaba/alicloud/oss/OssAutoConfiguration.java b/spring-cloud-alicloud-oss/src/main/java/com/alibaba/alicloud/oss/OssAutoConfiguration.java index 0cbf049e..2d87eddc 100644 --- a/spring-cloud-alicloud-oss/src/main/java/com/alibaba/alicloud/oss/OssAutoConfiguration.java +++ b/spring-cloud-alicloud-oss/src/main/java/com/alibaba/alicloud/oss/OssAutoConfiguration.java @@ -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 Jim */ @@ -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<>()); + } + } diff --git a/spring-cloud-alicloud-oss/src/main/java/com/alibaba/alicloud/oss/OssConstants.java b/spring-cloud-alicloud-oss/src/main/java/com/alibaba/alicloud/oss/OssConstants.java index d018b892..8bee73f6 100644 --- a/spring-cloud-alicloud-oss/src/main/java/com/alibaba/alicloud/oss/OssConstants.java +++ b/spring-cloud-alicloud-oss/src/main/java/com/alibaba/alicloud/oss/OssConstants.java @@ -17,13 +17,29 @@ package com.alibaba.alicloud.oss; /** - * OSS constants + * OSS constants. * * @author Jim */ -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"); + } } diff --git a/spring-cloud-alicloud-oss/src/main/java/com/alibaba/alicloud/oss/resource/OssStorageProtocolResolver.java b/spring-cloud-alicloud-oss/src/main/java/com/alibaba/alicloud/oss/resource/OssStorageProtocolResolver.java index 2d1f2d50..0a372e1d 100644 --- a/spring-cloud-alicloud-oss/src/main/java/com/alibaba/alicloud/oss/resource/OssStorageProtocolResolver.java +++ b/spring-cloud-alicloud-oss/src/main/java/com/alibaba/alicloud/oss/resource/OssStorageProtocolResolver.java @@ -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; } + } diff --git a/spring-cloud-alicloud-oss/src/main/java/com/alibaba/alicloud/oss/resource/OssStorageResource.java b/spring-cloud-alicloud-oss/src/main/java/com/alibaba/alicloud/oss/resource/OssStorageResource.java index 70825bae..6f84d2b5 100644 --- a/spring-cloud-alicloud-oss/src/main/java/com/alibaba/alicloud/oss/resource/OssStorageResource.java +++ b/spring-cloud-alicloud-oss/src/main/java/com/alibaba/alicloud/oss/resource/OssStorageResource.java @@ -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; + } + + } + } diff --git a/spring-cloud-alicloud-oss/src/test/java/com/alibaba/alicloud/oss/resource/DummyOssClient.java b/spring-cloud-alicloud-oss/src/test/java/com/alibaba/alicloud/oss/resource/DummyOssClient.java new file mode 100644 index 00000000..0d429975 --- /dev/null +++ b/spring-cloud-alicloud-oss/src/test/java/com/alibaba/alicloud/oss/resource/DummyOssClient.java @@ -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 storeMap = new ConcurrentHashMap<>(); + + private Map 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 bucketList() { + return new ArrayList<>(bucketSet.values()); + } + +} \ No newline at end of file diff --git a/spring-cloud-alicloud-oss/src/test/java/com/alibaba/alicloud/oss/resource/OssStorageResourceTest.java b/spring-cloud-alicloud-oss/src/test/java/com/alibaba/alicloud/oss/resource/OssStorageResourceTest.java new file mode 100644 index 00000000..2e977102 --- /dev/null +++ b/spring-cloud-alicloud-oss/src/test/java/com/alibaba/alicloud/oss/resource/OssStorageResourceTest.java @@ -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; + } + + } + +} \ No newline at end of file diff --git a/spring-cloud-starter-alibaba/pom.xml b/spring-cloud-starter-alibaba/pom.xml index 9cfb5133..d15c019d 100644 --- a/spring-cloud-starter-alibaba/pom.xml +++ b/spring-cloud-starter-alibaba/pom.xml @@ -20,5 +20,6 @@ spring-cloud-starter-stream-rocketmq spring-cloud-starter-bus-rocketmq spring-cloud-starter-dubbo + spring-cloud-starter-alibaba-sidecar \ No newline at end of file diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-sidecar/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-sidecar/pom.xml new file mode 100644 index 00000000..a3762eea --- /dev/null +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-sidecar/pom.xml @@ -0,0 +1,22 @@ + + + + com.alibaba.cloud + spring-cloud-starter-alibaba + 2.1.1.BUILD-SNAPSHOT + ../pom.xml + + 4.0.0 + + spring-cloud-starter-alibaba-sidecar + Spring Cloud Starter Alibaba Sidecar + + + + com.alibaba.cloud + spring-cloud-alibaba-sidecar + + + diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/RocketMQMessageChannelBinder.java b/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/RocketMQMessageChannelBinder.java index e580594e..74cf5db6 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/RocketMQMessageChannelBinder.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/RocketMQMessageChannelBinder.java @@ -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 Jim */ diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/consuming/RocketMQListenerBindingContainer.java b/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/consuming/RocketMQListenerBindingContainer.java index 8e00a249..d1bec14f 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/consuming/RocketMQListenerBindingContainer.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/consuming/RocketMQListenerBindingContainer.java @@ -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. *

* this class will delegate {@link RocketMQListener} to handle message * @@ -104,12 +104,16 @@ public class RocketMQListenerBindingContainer private final ExtendedConsumerProperties 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(); } } diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/properties/RocketMQBinderConfigurationProperties.java b/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/properties/RocketMQBinderConfigurationProperties.java index 4fda8c8f..e06a8690 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/properties/RocketMQBinderConfigurationProperties.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/properties/RocketMQBinderConfigurationProperties.java @@ -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 Jim */ @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; } + } diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/provisioning/selector/PartitionMessageQueueSelector.java b/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/provisioning/selector/PartitionMessageQueueSelector.java index 38603346..58938a03 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/provisioning/selector/PartitionMessageQueueSelector.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/provisioning/selector/PartitionMessageQueueSelector.java @@ -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 { diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/support/JacksonRocketMQHeaderMapper.java b/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/support/JacksonRocketMQHeaderMapper.java index 2c582b97..43a6870a 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/support/JacksonRocketMQHeaderMapper.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/support/JacksonRocketMQHeaderMapper.java @@ -20,22 +20,22 @@ 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 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; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.lang.Nullable; +import org.springframework.messaging.MessageHeaders; +import org.springframework.util.ClassUtils; /** * jackson header mapper for RocketMQ. Header types are added to a special header @@ -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 fromHeaders(MessageHeaders headers) { - final Map target = Maps.newHashMap(); - final Map jsonHeaders = Maps.newHashMap(); + final Map target = new HashMap<>(); + final Map 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 source) { - final Map target = Maps.newHashMap(); + final Map target = new HashMap<>(); final Map jsonTypes = decodeJsonTypes(source); source.forEach((key, value) -> { if (matches(key) && !(key.equals(JSON_TYPES))) { @@ -281,4 +282,5 @@ public class JacksonRocketMQHeaderMapper extends AbstractRocketMQHeaderMapper { } } + }