1
0
mirror of https://gitee.com/mirrors/Spring-Cloud-Alibaba.git synced 2021-06-26 13:25:11 +08:00

refactor dubbo examples & add datasource feature in example & update docs

This commit is contained in:
fangjian0423
2018-08-17 11:18:20 +08:00
parent d1fd1f0e2f
commit 6598768195
25 changed files with 784 additions and 186 deletions

View File

@@ -30,12 +30,6 @@
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
</dependencies>
<build>

View File

@@ -158,42 +158,81 @@ Sentinel 控制台支持实时监控查看,您可以通过 Sentinel 控制台
<p align="center"><img src="https://cdn.nlark.com/lark/0/2018/png/54319/1532313595369-8428cd7d-9eb7-4786-a149-acf0da4a2daf.png" width="480" heigh='180' ></p>
## 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的属性。
spring.cloud.sentinel.datasource.configParser代表ConfigParser在Spring容器里的name。如果没找到会抛出异常。
type目前支持file, nacos, zk, apollo。
### 自定义DataSource
自定义DataSource只需要两部。
1. 定义DataSource
public class CustomDataSource implements DataSource {
private String fieldA;
private String fieldB;
...
}
}
2. 装配DataSource。有两种方式处理。
在Provider端进行限流的话Consumer端调用的话会抛出RpcException因为Provider端被限流抛出了SentinelRpcException
* 直接构造DataSource
@Bean
public CustomDataSource customDataSource() {
CustomDataSource customDataSource =
new CustomDataSource();
customDataSource.setFieldA("valueA");
customDataSource.setFieldB("valueB");
...
return customDataSource;
}
### 应用启动
在启动ServiceApplication的前提下再启动ConsumerApplication
ConsumerApplication在Consumer端设置的限流规则所以启动完成后查看控制台的打印信息会发现部分调用被Block
* 在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)。

View File

@@ -165,42 +165,78 @@ To see the metrics, click **实时监控(Real-time Monitoring)** in the left-sid
<p align="center"><img src="https://cdn.nlark.com/lark/0/2018/png/54319/1532313595369-8428cd7d-9eb7-4786-a149-acf0da4a2daf.png" width="480" heigh='180'></p>
## 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.
### User-defined DataSource
User-defined DataSource need 2 steps.
1. Define DataSource
public class CustomDataSource implements DataSource {
private String fieldA;
private String fieldB;
...
}
}
2. Assemble DataSource. There are 2 ways to do this.
It will throw RpcException on Consumer side if it was be limited on Provider side, because Provider side throw SentinelRpcException in this scene.
* Construct DataSource directly
@Bean
public CustomDataSource customDataSource() {
CustomDataSource customDataSource = new CustomDataSource();
customDataSource.setFieldA("valueA");
customDataSource.setFieldB("valueB");
...
return customDataSource;
}
### Start Application
* 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.
You can startup ConsumerApplication after ServiceApplication startup.
ConsumerApplication init flow control rules after startup, so you will find some invocations have been blocked in console.
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).

View File

@@ -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<String, List<FlowRule>> {
@Override
public List<FlowRule> parse(String source) {
return JSON.parseObject(source, new TypeReference<List<FlowRule>>() {});
}
}

View File

@@ -1,5 +1,6 @@
package org.springframework.cloud.alibaba.cloud.examples;
import com.alibaba.csp.sentinel.datasource.ConfigParser;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect;
@@ -23,6 +24,11 @@ public class ServiceApplication {
return new RestTemplate();
}
@Bean
public ConfigParser myParser() {
return new JsonFlowRuleListParser();
}
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}

View File

@@ -1,24 +0,0 @@
package org.springframework.cloud.alibaba.cloud.examples.dubbo;
import org.springframework.boot.Banner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.WebApplicationType;
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(WebApplicationType.NONE);
providerBuilder.sources(ProviderApplication.class).run(args);
}
}

View File

@@ -1,10 +0,0 @@
package org.springframework.cloud.alibaba.cloud.examples.dubbo;
/**
* @author fangjian
*/
public interface FooService {
String hello(String name);
}

View File

@@ -1,83 +0,0 @@
package org.springframework.cloud.alibaba.cloud.examples.dubbo.consumer;
import java.util.Collections;
import org.springframework.boot.Banner;
import org.springframework.boot.WebApplicationType;
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(WebApplicationType.NONE);
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();
}
}
}
}

View File

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

View File

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

View File

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

View File

@@ -2,4 +2,13 @@ spring.application.name=sentinel-example
server.port=18083
management.endpoints.web.exposure.include=*
spring.cloud.sentinel.port=8721
spring.cloud.sentinel.dashboard=localhost:8080
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