diff --git a/spring-cloud-alibaba-dependencies/pom.xml b/spring-cloud-alibaba-dependencies/pom.xml index fad353f13..1c39170ae 100644 --- a/spring-cloud-alibaba-dependencies/pom.xml +++ b/spring-cloud-alibaba-dependencies/pom.xml @@ -32,6 +32,21 @@ sentinel-datasource-extension ${sentinel.version} + + com.alibaba.csp + sentinel-datasource-apollo + ${sentinel.version} + + + com.alibaba.csp + sentinel-datasource-zookeeper + ${sentinel.version} + + + com.alibaba.csp + sentinel-datasource-nacos + ${sentinel.version} + com.alibaba.csp sentinel-web-servlet diff --git a/spring-cloud-alibaba-examples/pom.xml b/spring-cloud-alibaba-examples/pom.xml index 24450ae2e..f5205129b 100644 --- a/spring-cloud-alibaba-examples/pom.xml +++ b/spring-cloud-alibaba-examples/pom.xml @@ -16,6 +16,9 @@ sentinel-example + sentinel-dubbo-provider-example + sentinel-dubbo-consumer-example + sentinel-dubbo-api storage-example diff --git a/spring-cloud-alibaba-examples/sentinel-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/pom.xml index 5348d778a..c15c79575 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/pom.xml +++ b/spring-cloud-alibaba-examples/sentinel-example/pom.xml @@ -30,12 +30,6 @@ spring-boot-starter-actuator - - com.alibaba.boot - dubbo-spring-boot-starter - 0.1.1 - - diff --git a/spring-cloud-alibaba-examples/sentinel-example/readme-zh.md b/spring-cloud-alibaba-examples/sentinel-example/readme-zh.md index 048f18c49..e39bf75d8 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/readme-zh.md +++ b/spring-cloud-alibaba-examples/sentinel-example/readme-zh.md @@ -157,42 +157,81 @@ Sentinel 控制台支持实时监控查看,您可以通过 Sentinel 控制台

-## Dubbo支持 +## DataSource支持 -[Dubbo](http://dubbo.apache.org/)是一款高性能Java RPC框架。 +Sentinel内部提供了[动态规则的扩展实现DataSource](https://github.com/alibaba/Sentinel/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%99%E6%89%A9%E5%B1%95#datasource-%E6%89%A9%E5%B1%95)。 -Sentinel提供了[sentinel-dubbo-adapter](https://github.com/alibaba/Sentinel/tree/master/sentinel-adapter/sentinel-dubbo-adapter)模块用来支持Dubbo服务调用的限流降级。sentinel-starter默认也集成了该功能。 +Sentinel starter整合了目前存在的4类DataSource。只需要在配置文件中进行相关配置,即可在Spring容器中自动注册DataSource。 -比如有个FooService服务,定义如下: +比如要定义一个FileRefreshableDataSource,配置如下: - package org.springframework.cloud.alibaba.cloud.examples.dubbo.FooService; - public interface FooService { - String hello(String name); - } + spring.cloud.sentinel.datasource.type=file + spring.cloud.sentinel.datasource.recommendRefreshMs=2000 + spring.cloud.sentinel.datasource.bufSize=2048 + spring.cloud.sentinel.datasource.charset=utf-8 + spring.cloud.sentinel.datasource.configParser=myParser + spring.cloud.sentinel.datasource.file=/Users/you/rule.json + +然后使用`@SentinelDataSource`注解修饰DataSource即可注入: + + @SentinelDataSource("spring.cloud.sentinel.datasource") + private DataSource dataSource; + +`@SentinelDataSource`注解的value属性可以不填。默认值就是spring.cloud.sentinel.datasource。 -该服务在Sentinel下对应的资源名是 `org.springframework.cloud.alibaba.cloud.examples.dubbo.FooService:hello(java.lang.String)` 。 +value属性代表配置前缀。示例中会去找spring.cloud.sentinel.datasource.xxx相关的配置。 -在Consumer端进行限流的话,需要处理SentinelRpcException。 +spring.cloud.sentinel.datasource.type就是对应的DataSource类型。 - FooService service = applicationContext.getBean(FooService.class); - - for (int i = 0; i < 15; i++) { - try { - String message = service.hello("Jim"); - } catch (SentinelRpcException ex) { - System.out.println("Blocked"); - } catch (Exception ex) { - ex.printStackTrace(); - } - } +spring.cloud.sentinel.datasource.recommendRefreshMs里的recommendRefreshMs对应相关DataSource的属性。 -在Provider端进行限流的话,Consumer端调用的话会抛出RpcException。因为Provider端被限流抛出了SentinelRpcException。 +spring.cloud.sentinel.datasource.configParser代表ConfigParser在Spring容器里的name。如果没找到,会抛出异常。 + +type目前支持file, nacos, zk, apollo。 -### Dubbo 应用启动 +### 自定义DataSource -在启动ServiceApplication的前提下,再启动ConsumerApplication。 +自定义DataSource只需要两部。 -ConsumerApplication在Consumer端设置了限流规则,所以启动完成后查看控制台的打印信息,会发现部分调用被Block。 +1. 定义DataSource + + public class CustomDataSource implements DataSource { + private String fieldA; + private String fieldB; + ... + } + +2. 装配DataSource。有两种方式处理。 + + * 直接构造DataSource + + @Bean + public CustomDataSource customDataSource() { + CustomDataSource customDataSource = + new CustomDataSource(); + customDataSource.setFieldA("valueA"); + customDataSource.setFieldB("valueB"); + ... + return customDataSource; + } + + * 在classpath:/META-INF/sentinel-datasource.properties中管理DataSource信息 + + custom = yourpackage.CustomDataSource + + 在application.properties中定义DataSource + + spring.cloud.sentinel.datasource.type = custom + spring.cloud.sentinel.datasource.fieldA = valueA + spring.cloud.sentinel.datasource.fieldB = valueB + + 注意:由于目前Sentinel的AbstractDataSource需要有个ConfigParser作为构造函数中的参数,并且它的子类的构造都是通过多个参数的构造函数构造的。 + 所以目前所有的Sentinel starter中的DataSource都是基于FactoryBean并且通过设置属性构造的。如果有这方面的需求,需要再多加一个registerFactoryBean过程。 + + SentinelDataSourceRegistry.registerFactoryBean("custeom", CustomDataSourceFactoryBean.class); + + 如果自定义DataSource可以注入属性,那么没有必要使用SentinelDataSourceRegistry注册FactoryBean。 + ## More Sentinel 是一款功能强大的中间件,从流量控制,熔断降级,系统负载保护等多个维度保护服务的稳定性。此 Demo 仅演示了 使用 Sentinel 作为限流工具的使用,更多 Sentinel 相关的信息,请参考 [Sentinel 项目](https://github.com/alibaba/Sentinel)。 diff --git a/spring-cloud-alibaba-examples/sentinel-example/readme.md b/spring-cloud-alibaba-examples/sentinel-example/readme.md index c9d61b25f..c1c3fb37b 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/readme.md +++ b/spring-cloud-alibaba-examples/sentinel-example/readme.md @@ -165,42 +165,77 @@ To see the metrics, click **实时监控(Real-time Monitoring)** in the left-sid

-## Dubbo +## DataSource -[Dubbo](http://dubbo.apache.org/) is a high-performance, java based open source RPC framework. +Sentinel provide [DataSource](https://github.com/alibaba/Sentinel/blob/master/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/DataSource.java) to manage dynamic rules. -Sentinel provide a module named [sentinel-dubbo-adapter](https://github.com/alibaba/Sentinel/tree/master/sentinel-adapter/sentinel-dubbo-adapter) to support dubbo.sentinel-starter integrates this feature by default. +Sentinel starter integrated 4 DataSources provided by Sentinel. It will be register into Spring Context if you write some configs in `application.properties`. -For example, a service named FooService, see the code below: +If you want to define FileRefreshableDataSource: - package org.springframework.cloud.alibaba.cloud.examples.dubbo.FooService; - public interface FooService { - String hello(String name); - } + spring.cloud.sentinel.datasource.type=file + spring.cloud.sentinel.datasource.recommendRefreshMs=2000 + spring.cloud.sentinel.datasource.bufSize=2048 + spring.cloud.sentinel.datasource.charset=utf-8 + spring.cloud.sentinel.datasource.configParser=myParser + spring.cloud.sentinel.datasource.file=/Users/you/rule.json + +then use `@SentinelDataSource` to annotate DataSource: + + @SentinelDataSource("spring.cloud.sentinel.datasource") + private DataSource dataSource; + +The value() of `@SentinelDataSource` is not required, it means the prefix of configuration. Default value is `spring.cloud.sentinel.datasource`. -The resource name of this service is `org.springframework.cloud.alibaba.cloud.examples.dubbo.FooService:hello(java.lang.String)` . +spring.cloud.sentinel.datasource.type means the type of DataSource. -You should handle SentinelRpcException if rpc invocation was be limited on Consumer side: +spring.cloud.sentinel.datasource.recommendRefreshMs means the recommendRefreshMs property of specified DataSource. - FooService service = applicationContext.getBean(FooService.class); - - for (int i = 0; i < 15; i++) { - try { - String message = service.hello("Jim"); - } catch (SentinelRpcException ex) { - System.out.println("Blocked"); - } catch (Exception ex) { - ex.printStackTrace(); - } - } +spring.cloud.sentinel.datasource.configParser means the name of spring bean that type is ConfigParser. If the bean is not exists, will throw exception. + +Now datasource type support 4 categories: file, nacos, zk, apollo. -It will throw RpcException on Consumer side if it was be limited on Provider side, because Provider side throw SentinelRpcException in this scene. +### User-defined DataSource -### Dubbo Start Application +User-defined DataSource need 2 steps. -You can startup ConsumerApplication after ServiceApplication startup. +1. Define DataSource + + public class CustomDataSource implements DataSource { + private String fieldA; + private String fieldB; + ... + } + +2. Assemble DataSource. There are 2 ways to do this. + + * Construct DataSource directly + + @Bean + public CustomDataSource customDataSource() { + CustomDataSource customDataSource = new CustomDataSource(); + customDataSource.setFieldA("valueA"); + customDataSource.setFieldB("valueB"); + ... + return customDataSource; + } -ConsumerApplication init flow control rules after startup, so you will find some invocations have been blocked in console. + * define DataSource metadata in `classpath:/META-INF/sentinel-datasource.properties` + + custom = yourpackage.CustomDataSource + + define configuration in `application.properties` + + spring.cloud.sentinel.datasource.type = custom + spring.cloud.sentinel.datasource.fieldA = valueA + spring.cloud.sentinel.datasource.fieldB = valueB + +Note: The AbstractDataSource of Sentinel need a ConfigParser as a constructor param and the subclass of AbstractDataSource was construct by multi-param constructor. +Now All DataSources in starter was construct by FactoryBean. If you want to do it in this way, you should register FactoryBean by SentinelDataSourceRegistry. + + SentinelDataSourceRegistry.registerFactoryBean("custeom", CustomDataSourceFactoryBean.class); + +It is no need to using SentinelDataSourceRegistry to register FactoryBean if your User-defined DataSource can inject fields. ## More For more information about Sentinel, see [Sentinel Project](https://github.com/alibaba/Sentinel). diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/JsonFlowRuleListParser.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/JsonFlowRuleListParser.java new file mode 100644 index 000000000..255eb7892 --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/JsonFlowRuleListParser.java @@ -0,0 +1,18 @@ +package org.springframework.cloud.alibaba.cloud.examples; + +import java.util.List; + +import com.alibaba.csp.sentinel.datasource.ConfigParser; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; + +/** + * @author fangjian + */ +public class JsonFlowRuleListParser implements ConfigParser> { + @Override + public List parse(String source) { + return JSON.parseObject(source, new TypeReference>() {}); + } +} diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ServiceApplication.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ServiceApplication.java index 9d1696a94..c9df5fc78 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ServiceApplication.java +++ b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ServiceApplication.java @@ -6,22 +6,29 @@ import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; +import com.alibaba.csp.sentinel.datasource.ConfigParser; + /** * @author xiaojing */ @SpringBootApplication public class ServiceApplication { - @Bean - @SentinelProtect(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class) - public RestTemplate restTemplate() { - return new RestTemplate(); - } + @Bean + @SentinelProtect(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class) + public RestTemplate restTemplate() { + return new RestTemplate(); + } - @Bean - public RestTemplate restTemplate2() { - return new RestTemplate(); - } + @Bean + public RestTemplate restTemplate2() { + return new RestTemplate(); + } + + @Bean + public ConfigParser myParser() { + return new JsonFlowRuleListParser(); + } public static void main(String[] args) { SpringApplication.run(ServiceApplication.class, args); diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/DubboProviderRunner.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/DubboProviderRunner.java deleted file mode 100644 index 4c683cddf..000000000 --- a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/DubboProviderRunner.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.springframework.cloud.alibaba.cloud.examples.dubbo; - -import org.springframework.boot.Banner; -import org.springframework.boot.CommandLineRunner; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.cloud.alibaba.cloud.examples.dubbo.provider.ProviderApplication; -import org.springframework.stereotype.Component; - -/** - * @author fangjian - */ -@Component -public class DubboProviderRunner implements CommandLineRunner { - - @Override - public void run(String... args) throws Exception { - SpringApplicationBuilder providerBuilder = new SpringApplicationBuilder() - .bannerMode(Banner.Mode.OFF).registerShutdownHook(false) - .logStartupInfo(false).web(false); - providerBuilder.sources(ProviderApplication.class).run(args); - } - -} diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/FooService.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/FooService.java deleted file mode 100644 index d44b5c996..000000000 --- a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/FooService.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.springframework.cloud.alibaba.cloud.examples.dubbo; - -/** - * @author fangjian - */ -public interface FooService { - - String hello(String name); - -} diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/consumer/ConsumerApplication.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/consumer/ConsumerApplication.java deleted file mode 100644 index 05e3d4b2d..000000000 --- a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/consumer/ConsumerApplication.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.springframework.cloud.alibaba.cloud.examples.dubbo.consumer; - -import java.util.Collections; - -import org.springframework.boot.Banner; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; - -import com.alibaba.csp.sentinel.slots.block.RuleConstant; -import com.alibaba.csp.sentinel.slots.block.SentinelRpcException; -import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; -import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; -import com.alibaba.dubbo.config.ApplicationConfig; -import com.alibaba.dubbo.config.ConsumerConfig; -import com.alibaba.dubbo.config.RegistryConfig; -import com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScan; - -/** - * @author fangjian - */ -@DubboComponentScan("org.springframework.cloud.alibaba.cloud.examples.dubbo.provider") -public class ConsumerApplication { - - @Bean - public ApplicationConfig applicationConfig() { - ApplicationConfig applicationConfig = new ApplicationConfig(); - applicationConfig.setName("demo-consumer"); - return applicationConfig; - } - - @Bean - public RegistryConfig registryConfig() { - RegistryConfig registryConfig = new RegistryConfig(); - registryConfig.setAddress("multicast://224.5.6.7:1234"); - return registryConfig; - } - - @Bean - public ConsumerConfig consumerConfig() { - ConsumerConfig consumerConfig = new ConsumerConfig(); - return consumerConfig; - } - - @Bean - public FooServiceConsumer annotationDemoServiceConsumer() { - return new FooServiceConsumer(); - } - - public static void main(String[] args) { - - SpringApplicationBuilder consumerBuilder = new SpringApplicationBuilder() - .bannerMode(Banner.Mode.OFF).registerShutdownHook(false) - .logStartupInfo(false).web(false); - ApplicationContext applicationContext = consumerBuilder - .sources(ConsumerApplication.class).run(args); - - FlowRule flowRule = new FlowRule(); - flowRule.setResource( - "org.springframework.cloud.alibaba.cloud.examples.dubbo.FooService:hello(java.lang.String)"); - flowRule.setCount(10); - flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS); - flowRule.setLimitApp("default"); - FlowRuleManager.loadRules(Collections.singletonList(flowRule)); - - FooServiceConsumer service = applicationContext.getBean(FooServiceConsumer.class); - - for (int i = 0; i < 15; i++) { - try { - String message = service.hello("Jim"); - System.out.println((i + 1) + " -> Success: " + message); - } - catch (SentinelRpcException ex) { - System.out.println("Blocked"); - } - catch (Exception ex) { - ex.printStackTrace(); - } - } - } - -} diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/consumer/FooServiceConsumer.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/consumer/FooServiceConsumer.java deleted file mode 100644 index 2fe62bf5a..000000000 --- a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/consumer/FooServiceConsumer.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.springframework.cloud.alibaba.cloud.examples.dubbo.consumer; - -import org.springframework.cloud.alibaba.cloud.examples.dubbo.FooService; - -import com.alibaba.dubbo.config.annotation.Reference; - -/** - * @author fangjian - */ -public class FooServiceConsumer { - - @Reference(url = "dubbo://127.0.0.1:25758", timeout = 3000) - private FooService fooService; - - public String hello(String name) { - return fooService.hello(name); - } - -} diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/provider/FooServiceImpl.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/provider/FooServiceImpl.java deleted file mode 100644 index bb9e81d66..000000000 --- a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/provider/FooServiceImpl.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.springframework.cloud.alibaba.cloud.examples.dubbo.provider; - -import org.springframework.cloud.alibaba.cloud.examples.dubbo.FooService; - -import com.alibaba.dubbo.config.annotation.Service; - -/** - * @author fangjian - */ -@Service -public class FooServiceImpl implements FooService { - - @Override - public String hello(String name) { - return "hello, " + name; - } -} diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/provider/ProviderApplication.java b/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/provider/ProviderApplication.java deleted file mode 100644 index 6f793bb6e..000000000 --- a/spring-cloud-alibaba-examples/sentinel-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/dubbo/provider/ProviderApplication.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.springframework.cloud.alibaba.cloud.examples.dubbo.provider; - -import org.springframework.context.annotation.Bean; - -import com.alibaba.dubbo.config.ApplicationConfig; -import com.alibaba.dubbo.config.ProtocolConfig; -import com.alibaba.dubbo.config.RegistryConfig; -import com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScan; - -/** - * @author fangjian - */ -@DubboComponentScan("org.springframework.cloud.alibaba.cloud.examples.dubbo.provider") -public class ProviderApplication { - - @Bean - public ApplicationConfig applicationConfig() { - ApplicationConfig applicationConfig = new ApplicationConfig(); - applicationConfig.setName("demo-provider"); - return applicationConfig; - } - - @Bean - public RegistryConfig registryConfig() { - RegistryConfig registryConfig = new RegistryConfig(); - registryConfig.setAddress("multicast://224.5.6.7:1234"); - return registryConfig; - } - - @Bean - public ProtocolConfig protocolConfig() { - ProtocolConfig protocolConfig = new ProtocolConfig(); - protocolConfig.setName("dubbo"); - protocolConfig.setPort(25758); - return protocolConfig; - } - -} diff --git a/spring-cloud-alibaba-examples/sentinel-example/src/main/resources/application.properties b/spring-cloud-alibaba-examples/sentinel-example/src/main/resources/application.properties index 252f0fdda..f8fa03ee6 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/src/main/resources/application.properties +++ b/spring-cloud-alibaba-examples/sentinel-example/src/main/resources/application.properties @@ -2,4 +2,13 @@ spring.application.name=sentinel-example server.port=18083 management.security.enabled=false spring.cloud.sentinel.port=8721 -spring.cloud.sentinel.dashboard=localhost:8080 \ No newline at end of file +spring.cloud.sentinel.dashboard=localhost:8080 + + + +spring.cloud.sentinel.datasource.type=file +spring.cloud.sentinel.datasource.recommendRefreshMs=3000 +spring.cloud.sentinel.datasource.bufSize=4056196 +spring.cloud.sentinel.datasource.charset=utf-8 +spring.cloud.sentinel.datasource.configParser=myParser +spring.cloud.sentinel.datasource.file=/Users/you/rule.json \ No newline at end of file diff --git a/spring-cloud-alibaba-sentinel/pom.xml b/spring-cloud-alibaba-sentinel/pom.xml index cea895a76..31dd8e7b2 100644 --- a/spring-cloud-alibaba-sentinel/pom.xml +++ b/spring-cloud-alibaba-sentinel/pom.xml @@ -25,6 +25,29 @@ sentinel-transport-simple-http + + com.alibaba.csp + sentinel-datasource-extension + + + + com.alibaba.csp + sentinel-datasource-nacos + true + + + + com.alibaba.csp + sentinel-datasource-zookeeper + true + + + + com.alibaba.csp + sentinel-datasource-apollo + true + + com.alibaba.csp sentinel-annotation-aspectj diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelEntry.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelConstants.java similarity index 53% rename from spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelEntry.java rename to spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelConstants.java index fdc4f3bf6..8f0318e39 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelEntry.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelConstants.java @@ -14,41 +14,20 @@ * limitations under the License. */ -package org.springframework.cloud.alibaba.sentinel.custom; - -import com.alibaba.csp.sentinel.Entry; +package org.springframework.cloud.alibaba.sentinel; /** - * @author xiaojing + * @author fangjian */ -public class SentinelEntry { - - private String key; - private String handler; - - private Entry entry; - - public String getKey() { - return key; - } +public interface SentinelConstants { - public void setKey(String key) { - this.key = key; - } + String PROPERTY_PREFIX = "spring.cloud.sentinel"; - public String getHandler() { - return handler; - } + String PROPERTY_ITEM_SEPARATOR = "."; - public void setHandler(String handler) { - this.handler = handler; - } + String PROPERTY_DATASOURCE_NAME = "datasource"; - public Entry getEntry() { - return entry; - } + String PROPERTY_DATASOURCE_PREFIX = PROPERTY_PREFIX + PROPERTY_ITEM_SEPARATOR + + PROPERTY_DATASOURCE_NAME; - public void setEntry(Entry entry) { - this.entry = entry; - } } diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelProperties.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelProperties.java index 283ea0705..eb1f2be77 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelProperties.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelProperties.java @@ -26,7 +26,7 @@ import org.springframework.core.Ordered; * @author xiaojing * @author hengyunabc */ -@ConfigurationProperties(prefix = "spring.cloud.sentinel") +@ConfigurationProperties(prefix = SentinelConstants.PROPERTY_PREFIX) public class SentinelProperties { /** diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelWebAutoConfiguration.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelWebAutoConfiguration.java index 81528668d..d588e3372 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelWebAutoConfiguration.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelWebAutoConfiguration.java @@ -16,25 +16,22 @@ package org.springframework.cloud.alibaba.sentinel; -import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter; import java.util.ArrayList; import java.util.List; -import com.alibaba.csp.sentinel.transport.config.TransportConfig; +import javax.servlet.Filter; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.util.StringUtils; -import javax.annotation.PostConstruct; -import javax.servlet.Filter; +import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter; /** * @author xiaojing @@ -47,30 +44,10 @@ public class SentinelWebAutoConfiguration { private static final Logger logger = LoggerFactory .getLogger(SentinelWebAutoConfiguration.class); - @Value("${project.name:${spring.application.name:}}") - private String projectName; - @Autowired private SentinelProperties properties; - public static final String APP_NAME = "project.name"; - - @PostConstruct - private void init() { - if (StringUtils.isEmpty(System.getProperty(APP_NAME))) { - System.setProperty(APP_NAME, projectName); - } - if (StringUtils.isEmpty(System.getProperty(TransportConfig.SERVER_PORT))) { - System.setProperty(TransportConfig.SERVER_PORT, properties.getPort()); - } - if (StringUtils.isEmpty(System.getProperty(TransportConfig.CONSOLE_SERVER))) { - System.setProperty(TransportConfig.CONSOLE_SERVER, properties.getDashboard()); - } - - } - @Bean - @ConditionalOnWebApplication public FilterRegistrationBean servletRequestListener() { FilterRegistrationBean registration = new FilterRegistrationBean(); diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/annotation/SentinelDataSource.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/annotation/SentinelDataSource.java new file mode 100644 index 000000000..73a8ee173 --- /dev/null +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/annotation/SentinelDataSource.java @@ -0,0 +1,44 @@ +/* + * 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 org.springframework.cloud.alibaba.sentinel.annotation; + +import java.lang.annotation.*; + +import org.springframework.core.annotation.AliasFor; + +/** + * An annotation to inject {@link com.alibaba.csp.sentinel.datasource.DataSource} instance + * into a Spring Bean. The Properties of DataSource bean get from config files with + * specific prefix. + * + * @author fangjian + * @see com.alibaba.csp.sentinel.datasource.DataSource + */ +@Target({ ElementType.FIELD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface SentinelDataSource { + + @AliasFor("prefix") + String value() default ""; + + @AliasFor("value") + String prefix() default ""; + + String name() default ""; // spring bean name + +} diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java index c473d94b4..2191f6a32 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java @@ -16,20 +16,52 @@ package org.springframework.cloud.alibaba.sentinel.custom; +import com.alibaba.csp.sentinel.transport.config.TransportConfig; +import com.alibaba.csp.sentinel.util.AppNameUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.alibaba.sentinel.SentinelProperties; +import org.springframework.cloud.alibaba.sentinel.datasource.SentinelDataSourcePostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect; +import javax.annotation.PostConstruct; + /** * @author xiaojing */ @Configuration +@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true) +@EnableConfigurationProperties(SentinelProperties.class) public class SentinelAutoConfiguration { + @Value("${project.name:${spring.application.name:}}") + private String projectName; + + @Autowired + private SentinelProperties properties; + + @PostConstruct + private void init() { + if (StringUtils.isEmpty(System.getProperty(AppNameUtil.APP_NAME))) { + System.setProperty(AppNameUtil.APP_NAME, projectName); + } + if (StringUtils.isEmpty(System.getProperty(TransportConfig.SERVER_PORT))) { + System.setProperty(TransportConfig.SERVER_PORT, properties.getPort()); + } + if (StringUtils.isEmpty(System.getProperty(TransportConfig.CONSOLE_SERVER))) { + System.setProperty(TransportConfig.CONSOLE_SERVER, properties.getDashboard()); + } + } + @Bean @ConditionalOnMissingBean public SentinelResourceAspect sentinelResourceAspect() { @@ -38,9 +70,15 @@ public class SentinelAutoConfiguration { @Bean @ConditionalOnMissingBean - @ConditionalOnClass(value = RestTemplate.class) + @ConditionalOnClass(name = "org.springframework.web.client.RestTemplate") public SentinelBeanPostProcessor sentinelBeanPostProcessor() { return new SentinelBeanPostProcessor(); } + @Bean + @ConditionalOnMissingBean + public SentinelDataSourcePostProcessor sentinelDataSourcePostProcessor() { + return new SentinelDataSourcePostProcessor(); + } + } diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java index e48616489..dc22262f4 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java @@ -43,7 +43,7 @@ import com.alibaba.csp.sentinel.util.StringUtil; */ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor { - private static final Logger LOGGER = LoggerFactory + private static final Logger logger = LoggerFactory .getLogger(SentinelProtectInterceptor.class); private SentinelProtect sentinelProtect; @@ -68,12 +68,12 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor response = execution.execute(request, body); } catch (BlockException e) { - LOGGER.error("RestTemplate block", e); + logger.error("RestTemplate block", e); try { handleBlockException(e); } catch (Exception ex) { - LOGGER.error("sentinel handle BlockException error.", e); + logger.error("sentinel handle BlockException error.", e); } } finally { diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/DataSourceLoader.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/DataSourceLoader.java new file mode 100644 index 000000000..183477cb5 --- /dev/null +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/DataSourceLoader.java @@ -0,0 +1,151 @@ +/* + * 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 org.springframework.cloud.alibaba.sentinel.datasource; + +import static org.springframework.core.io.support.ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.PropertiesLoaderUtils; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; + +import com.alibaba.csp.sentinel.datasource.DataSource; + +/** + * {@link DataSource} Loader + * + * @author fangjian + */ +public class DataSourceLoader { + + private static final Logger logger = LoggerFactory.getLogger(DataSourceLoader.class); + + private final static String PROPERTIES_RESOURCE_LOCATION = "META-INF/sentinel-datasource.properties"; + + private final static String ALL_PROPERTIES_RESOURCES_LOCATION = CLASSPATH_ALL_URL_PREFIX + + PROPERTIES_RESOURCE_LOCATION; + + private final static ConcurrentMap> dataSourceClassesCache = new ConcurrentHashMap>( + 4); + + static void loadAllDataSourceClassesCache() { + Map> dataSourceClassesMap = loadAllDataSourceClassesCache( + ALL_PROPERTIES_RESOURCES_LOCATION); + + dataSourceClassesCache.putAll(dataSourceClassesMap); + } + + static Map> loadAllDataSourceClassesCache( + String resourcesLocation) { + + Map> dataSourcesMap = new HashMap>( + 4); + + ClassLoader classLoader = DataSourceLoader.class.getClassLoader(); + + ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + + try { + + Resource[] resources = resolver.getResources(resourcesLocation); + + for (Resource resource : resources) { + if (resource.exists()) { + Properties properties = PropertiesLoaderUtils + .loadProperties(resource); + for (Map.Entry entry : properties.entrySet()) { + + String type = (String) entry.getKey(); + String className = (String) entry.getValue(); + + if (!ClassUtils.isPresent(className, classLoader)) { + if (logger.isDebugEnabled()) { + logger.debug( + "Sentinel DataSource implementation [ type : " + + type + ": , class : " + className + + " , url : " + resource.getURL() + + "] was not present in current classpath , " + + "thus loading will be ignored , please add dependency if required !"); + } + continue; + } + + Assert.isTrue(!dataSourcesMap.containsKey(type), + "The duplicated type[" + type + + "] of SentinelDataSource were found in " + + "resource [" + resource.getURL() + "]"); + + Class dataSourceClass = ClassUtils.resolveClassName(className, + classLoader); + Assert.isAssignable(DataSource.class, dataSourceClass); + + dataSourcesMap.put(type, + (Class) dataSourceClass); + + if (logger.isDebugEnabled()) { + logger.debug("Sentinel DataSource implementation [ type : " + + type + ": , class : " + className + + "] was loaded."); + } + } + } + } + + } + catch (IOException e) { + if (logger.isErrorEnabled()) { + logger.error(e.getMessage(), e); + } + } + + return dataSourcesMap; + } + + public static Class loadClass(String type) + throws IllegalArgumentException { + + Class dataSourceClass = dataSourceClassesCache.get(type); + + if (dataSourceClass == null) { + if (dataSourceClassesCache.isEmpty()) { + loadAllDataSourceClassesCache(); + dataSourceClass = dataSourceClassesCache.get(type); + } + } + + if (dataSourceClass == null) { + throw new IllegalArgumentException( + "Sentinel DataSource implementation [ type : " + type + + " ] can't be found!"); + } + + return dataSourceClass; + + } + +} diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourcePostProcessor.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourcePostProcessor.java new file mode 100644 index 000000000..54914702b --- /dev/null +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourcePostProcessor.java @@ -0,0 +1,217 @@ +/* + * 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 org.springframework.cloud.alibaba.sentinel.datasource; + +import static org.springframework.core.annotation.AnnotationUtils.getAnnotation; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.PropertyValues; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.cloud.alibaba.sentinel.SentinelConstants; +import org.springframework.cloud.alibaba.sentinel.annotation.SentinelDataSource; +import org.springframework.cloud.alibaba.sentinel.util.PropertySourcesUtils; +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; + +import com.alibaba.csp.sentinel.datasource.ConfigParser; + +/** + * {@link SentinelDataSource @SentinelDataSource} Post Processor + * + * @author fangjian + * @see com.alibaba.csp.sentinel.datasource.DataSource + * @see SentinelDataSource + */ +public class SentinelDataSourcePostProcessor + extends InstantiationAwareBeanPostProcessorAdapter + implements MergedBeanDefinitionPostProcessor { + + private static final Logger logger = LoggerFactory + .getLogger(SentinelDataSourcePostProcessor.class); + + @Autowired + private ApplicationContext applicationContext; + + @Autowired + private ConfigurableEnvironment environment; + + private final Map> dataSourceFieldCache = new ConcurrentHashMap<>( + 64); + + @Override + public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, + Class beanType, final String beanName) { + // find all fields using by @SentinelDataSource annotation + ReflectionUtils.doWithFields(beanType, new ReflectionUtils.FieldCallback() { + @Override + public void doWith(Field field) + throws IllegalArgumentException, IllegalAccessException { + SentinelDataSource annotation = getAnnotation(field, + SentinelDataSource.class); + if (annotation != null) { + if (Modifier.isStatic(field.getModifiers())) { + if (logger.isWarnEnabled()) { + logger.warn( + "@SentinelDataSource annotation is not supported on static fields: " + + field); + } + return; + } + if (dataSourceFieldCache.containsKey(beanName)) { + dataSourceFieldCache.get(beanName) + .add(new SentinelDataSourceField(annotation, field)); + } + else { + List list = new ArrayList<>(); + list.add(new SentinelDataSourceField(annotation, field)); + dataSourceFieldCache.put(beanName, list); + } + } + } + }); + } + + @Override + public PropertyValues postProcessPropertyValues(PropertyValues pvs, + PropertyDescriptor[] pds, Object bean, String beanName) + throws BeanCreationException { + if (dataSourceFieldCache.containsKey(beanName)) { + List sentinelDataSourceFields = dataSourceFieldCache + .get(beanName); + for(SentinelDataSourceField sentinelDataSourceField : sentinelDataSourceFields) { + try { + // construct DataSource field annotated by @SentinelDataSource + Field field = sentinelDataSourceField.getField(); + ReflectionUtils.makeAccessible(field); + String dataSourceBeanName = constructDataSource( + sentinelDataSourceField.getSentinelDataSource()); + field.set(bean, applicationContext.getBean(dataSourceBeanName)); + } + catch (IllegalAccessException e) { + e.printStackTrace(); + } + catch (Exception e) { + e.printStackTrace(); + } + } + } + return pvs; + } + + private String constructDataSource(SentinelDataSource annotation) { + String prefix = annotation.value(); + if (StringUtils.isEmpty(prefix)) { + prefix = SentinelConstants.PROPERTY_DATASOURCE_PREFIX; + } + Map propertyMap = PropertySourcesUtils + .getSubProperties(environment.getPropertySources(), prefix); + String alias = propertyMap.get("type").toString(); + Class dataSourceClass = DataSourceLoader.loadClass(alias); + + String beanName = StringUtils.isEmpty(annotation.name()) + ? StringUtils.uncapitalize(dataSourceClass.getSimpleName()) + "_" + prefix + : annotation.name(); + if (applicationContext.containsBean(beanName)) { + return beanName; + } + + Class targetClass = null; + // if alias exists in SentinelDataSourceRegistry, wired properties into + // FactoryBean + if (SentinelDataSourceRegistry.checkFactoryBean(alias)) { + targetClass = SentinelDataSourceRegistry.getFactoryBean(alias); + } + else { + // if alias not exists in SentinelDataSourceRegistry, wired properties into + // raw class + targetClass = dataSourceClass; + } + + registerDataSource(beanName, targetClass, propertyMap); + + return beanName; + } + + private void registerDataSource(String beanName, Class targetClass, + Map propertyMap) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder + .genericBeanDefinition(targetClass); + for (String propertyName : propertyMap.keySet()) { + Field field = ReflectionUtils.findField(targetClass, propertyName); + if (field != null) { + if (field.getType().isAssignableFrom(ConfigParser.class)) { + // ConfigParser get from ApplicationContext + builder.addPropertyReference(propertyName, + propertyMap.get(propertyName).toString()); + } + else { + // wired properties + builder.addPropertyValue(propertyName, propertyMap.get(propertyName)); + } + } + } + + DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext + .getAutowireCapableBeanFactory(); + beanFactory.registerBeanDefinition(beanName, builder.getBeanDefinition()); + } + + class SentinelDataSourceField { + private SentinelDataSource sentinelDataSource; + private Field field; + + public SentinelDataSourceField(SentinelDataSource sentinelDataSource, + Field field) { + this.sentinelDataSource = sentinelDataSource; + this.field = field; + } + + public SentinelDataSource getSentinelDataSource() { + return sentinelDataSource; + } + + public void setSentinelDataSource(SentinelDataSource sentinelDataSource) { + this.sentinelDataSource = sentinelDataSource; + } + + public Field getField() { + return field; + } + + public void setField(Field field) { + this.field = field; + } + } + +} diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourceRegistry.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourceRegistry.java new file mode 100644 index 000000000..f9917a806 --- /dev/null +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelDataSourceRegistry.java @@ -0,0 +1,67 @@ +/* + * 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 org.springframework.cloud.alibaba.sentinel.datasource; + +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ApolloDataSourceFactoryBean; +import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.FileRefreshableDataSourceFactoryBean; +import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.NacosDataSourceFactoryBean; +import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ZookeeperDataSourceFactoryBean; + +/** + * Registry to save DataSource FactoryBean + * + * @author fangjian + * @see com.alibaba.csp.sentinel.datasource.DataSource + * @see FileRefreshableDataSourceFactoryBean + * @see ZookeeperDataSourceFactoryBean + * @see NacosDataSourceFactoryBean + * @see ApolloDataSourceFactoryBean + */ +public class SentinelDataSourceRegistry { + + private static ConcurrentHashMap> cache = new ConcurrentHashMap<>( + 32); + + static { + SentinelDataSourceRegistry.registerFactoryBean("file", + FileRefreshableDataSourceFactoryBean.class); + SentinelDataSourceRegistry.registerFactoryBean("zk", + ZookeeperDataSourceFactoryBean.class); + SentinelDataSourceRegistry.registerFactoryBean("nacos", + NacosDataSourceFactoryBean.class); + SentinelDataSourceRegistry.registerFactoryBean("apollo", + ApolloDataSourceFactoryBean.class); + } + + public static synchronized void registerFactoryBean(String alias, + Class clazz) { + cache.putIfAbsent(alias, clazz); + cache.put(alias, clazz); + } + + public static Class getFactoryBean(String alias) { + return cache.get(alias); + } + + public static boolean checkFactoryBean(String alias) { + return cache.containsKey(alias); + } + +} diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/factorybean/ApolloDataSourceFactoryBean.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/factorybean/ApolloDataSourceFactoryBean.java new file mode 100644 index 000000000..927ba08e3 --- /dev/null +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/factorybean/ApolloDataSourceFactoryBean.java @@ -0,0 +1,66 @@ +package org.springframework.cloud.alibaba.sentinel.datasource.factorybean; + +import org.springframework.beans.factory.FactoryBean; + +import com.alibaba.csp.sentinel.datasource.ConfigParser; +import com.alibaba.csp.sentinel.datasource.apollo.ApolloDataSource; + +/** + * @author fangjian + * @see ApolloDataSource + */ +public class ApolloDataSourceFactoryBean implements FactoryBean { + + private String namespaceName; + private String flowRulesKey; + private String defaultFlowRuleValue; + private ConfigParser configParser; + + @Override + public ApolloDataSource getObject() throws Exception { + return new ApolloDataSource(namespaceName, flowRulesKey, defaultFlowRuleValue, + configParser); + } + + @Override + public Class getObjectType() { + return ApolloDataSource.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + public String getNamespaceName() { + return namespaceName; + } + + public void setNamespaceName(String namespaceName) { + this.namespaceName = namespaceName; + } + + public String getFlowRulesKey() { + return flowRulesKey; + } + + public void setFlowRulesKey(String flowRulesKey) { + this.flowRulesKey = flowRulesKey; + } + + public String getDefaultFlowRuleValue() { + return defaultFlowRuleValue; + } + + public void setDefaultFlowRuleValue(String defaultFlowRuleValue) { + this.defaultFlowRuleValue = defaultFlowRuleValue; + } + + public ConfigParser getConfigParser() { + return configParser; + } + + public void setConfigParser(ConfigParser configParser) { + this.configParser = configParser; + } +} diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/factorybean/FileRefreshableDataSourceFactoryBean.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/factorybean/FileRefreshableDataSourceFactoryBean.java new file mode 100644 index 000000000..e6265dd40 --- /dev/null +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/factorybean/FileRefreshableDataSourceFactoryBean.java @@ -0,0 +1,78 @@ +package org.springframework.cloud.alibaba.sentinel.datasource.factorybean; + +import java.io.File; +import java.nio.charset.Charset; + +import org.springframework.beans.factory.FactoryBean; + +import com.alibaba.csp.sentinel.datasource.ConfigParser; +import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource; + +/** + * @author fangjian + * @see FileRefreshableDataSource + */ +public class FileRefreshableDataSourceFactoryBean implements FactoryBean { + + private String file; + private String charset; + private long recommendRefreshMs; + private int bufSize; + private ConfigParser configParser; + + @Override + public FileRefreshableDataSource getObject() throws Exception { + return new FileRefreshableDataSource(new File(file), configParser, + recommendRefreshMs, bufSize, Charset.forName(charset)); + } + + @Override + public Class getObjectType() { + return FileRefreshableDataSource.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + public String getFile() { + return file; + } + + public void setFile(String file) { + this.file = file; + } + + public String getCharset() { + return charset; + } + + public void setCharset(String charset) { + this.charset = charset; + } + + public long getRecommendRefreshMs() { + return recommendRefreshMs; + } + + public void setRecommendRefreshMs(long recommendRefreshMs) { + this.recommendRefreshMs = recommendRefreshMs; + } + + public int getBufSize() { + return bufSize; + } + + public void setBufSize(int bufSize) { + this.bufSize = bufSize; + } + + public ConfigParser getConfigParser() { + return configParser; + } + + public void setConfigParser(ConfigParser configParser) { + this.configParser = configParser; + } +} diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/factorybean/NacosDataSourceFactoryBean.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/factorybean/NacosDataSourceFactoryBean.java new file mode 100644 index 000000000..8ebfc982a --- /dev/null +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/factorybean/NacosDataSourceFactoryBean.java @@ -0,0 +1,65 @@ +package org.springframework.cloud.alibaba.sentinel.datasource.factorybean; + +import org.springframework.beans.factory.FactoryBean; + +import com.alibaba.csp.sentinel.datasource.ConfigParser; +import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource; + +/** + * @author fangjian + * @see NacosDataSource + */ +public class NacosDataSourceFactoryBean implements FactoryBean { + + private String serverAddr; + private String groupId; + private String dataId; + private ConfigParser configParser; + + @Override + public NacosDataSource getObject() throws Exception { + return new NacosDataSource(serverAddr, groupId, dataId, configParser); + } + + @Override + public Class getObjectType() { + return NacosDataSource.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + public String getServerAddr() { + return serverAddr; + } + + public void setServerAddr(String serverAddr) { + this.serverAddr = serverAddr; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getDataId() { + return dataId; + } + + public void setDataId(String dataId) { + this.dataId = dataId; + } + + public ConfigParser getConfigParser() { + return configParser; + } + + public void setConfigParser(ConfigParser configParser) { + this.configParser = configParser; + } +} diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/factorybean/ZookeeperDataSourceFactoryBean.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/factorybean/ZookeeperDataSourceFactoryBean.java new file mode 100644 index 000000000..88dbb61e8 --- /dev/null +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/factorybean/ZookeeperDataSourceFactoryBean.java @@ -0,0 +1,85 @@ +package org.springframework.cloud.alibaba.sentinel.datasource.factorybean; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.FactoryBean; + +import com.alibaba.csp.sentinel.datasource.ConfigParser; +import com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource; + +/** + * @author fangjian + * @see ZookeeperDataSource + */ +public class ZookeeperDataSourceFactoryBean implements FactoryBean { + + private String serverAddr; + + private String path; + + private String groupId; + private String dataId; + + private ConfigParser configParser; + + @Override + public ZookeeperDataSource getObject() throws Exception { + if (StringUtils.isNotEmpty(groupId) && StringUtils.isNotEmpty(dataId)) { + // the path will be /{groupId}/{dataId} + return new ZookeeperDataSource(serverAddr, groupId, dataId, configParser); + } + else { + // using path directly + return new ZookeeperDataSource(serverAddr, path, configParser); + } + } + + @Override + public Class getObjectType() { + return ZookeeperDataSource.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + public String getServerAddr() { + return serverAddr; + } + + public void setServerAddr(String serverAddr) { + this.serverAddr = serverAddr; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getDataId() { + return dataId; + } + + public void setDataId(String dataId) { + this.dataId = dataId; + } + + public ConfigParser getConfigParser() { + return configParser; + } + + public void setConfigParser(ConfigParser configParser) { + this.configParser = configParser; + } +} diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/util/PropertySourcesUtils.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/util/PropertySourcesUtils.java new file mode 100644 index 000000000..6eafbd63b --- /dev/null +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/util/PropertySourcesUtils.java @@ -0,0 +1,75 @@ +/* + * 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 org.springframework.cloud.alibaba.sentinel.util; + +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.core.env.PropertySources; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Properties; + +/** + * {@link PropertySources} Utilities + * + * @author Mercy + */ +public abstract class PropertySourcesUtils { + + /** + * Get Sub {@link Properties} + * + * @param propertySources {@link PropertySource} Iterable + * @param prefix the prefix of property name + * @return Map + * @see Properties + */ + public static Map getSubProperties(Iterable> propertySources, String prefix) { + + Map subProperties = new LinkedHashMap(); + + String normalizedPrefix = normalizePrefix(prefix); + + for (PropertySource source : propertySources) { + if (source instanceof EnumerablePropertySource) { + for (String name : ((EnumerablePropertySource) source).getPropertyNames()) { + if (!subProperties.containsKey(name) && name.startsWith(normalizedPrefix)) { + String subName = name.substring(normalizedPrefix.length()); + if (!subProperties.containsKey(subName)) { // take first one + Object value = source.getProperty(name); + subProperties.put(subName, value); + } + } + } + } + } + + return subProperties; + + } + + /** + * Normalize the prefix + * + * @param prefix the prefix + * @return the prefix + */ + public static String normalizePrefix(String prefix) { + return prefix.endsWith(".") ? prefix : prefix + "."; + } +} diff --git a/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/sentinel-datasource.properties b/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/sentinel-datasource.properties new file mode 100644 index 000000000..8326eff63 --- /dev/null +++ b/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/sentinel-datasource.properties @@ -0,0 +1,4 @@ +nacos = com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource +file =com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource +apollo = com.alibaba.csp.sentinel.datasource.apollo.ApolloDataSource +zk = com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource \ No newline at end of file diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-sentinel/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-sentinel/pom.xml index bb23b749a..37d409592 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-sentinel/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-sentinel/pom.xml @@ -15,6 +15,18 @@ org.springframework.cloud spring-cloud-alibaba-sentinel + + com.alibaba.csp + sentinel-datasource-nacos + + + com.alibaba.csp + sentinel-datasource-zookeeper + + + com.alibaba.csp + sentinel-datasource-apollo +