Sentinel 配置持久化
规则参数
流控规则
配置
参数
Field |
说明 |
默认值 |
resource |
资源名,资源名是限流规则的作用对象 |
|
count |
限流阈值 |
|
grade |
限流阈值类型,QPS 或线程数模式 |
QPS 模式 |
limitApp |
流控针对的调用来源 |
default ,代表不区分调用来源 |
strategy |
判断的根据是资源自身,还是根据其它关联资源 (refResource ),还是根据链路入口 |
根据资源本身 |
controlBehavior |
流控效果(直接拒绝 / 排队等待 / 慢启动模式) |
直接拒绝 |
代码示例
1 2 3 4 5 6 7 8 9 10
| private void initFlowQpsRule() { List<FlowRule> rules = new ArrayList<>(); FlowRule rule = new FlowRule(resourceName); rule.setCount(20); rule.setGrade(RuleConstant.FLOW_GRADE_QPS); rule.setLimitApp("default"); rules.add(rule); FlowRuleManager.loadRules(rules); }
|
降级规则
配置
参数
|
|
|
Field |
说明 |
默认值 |
resource |
资源名,即限流规则的作用对象 |
|
count |
阈值 |
|
grade |
降级模式,根据 RT 降级还是根据异常比例降级 |
RT |
timeWindow |
降级的时间,单位为 s |
|
代码配置示例
1 2 3 4 5 6 7 8 9 10 11
| private void initDegradeRule() { List<DegradeRule> rules = new ArrayList<>(); DegradeRule rule = new DegradeRule(); rule.setResource(KEY); rule.setCount(10); rule.setGrade(RuleConstant.DEGRADE_GRADE_RT); rule.setTimeWindow(10); rules.add(rule); DegradeRuleManager.loadRules(rules); }
|
热点规则
配置
参数
属性 |
说明 |
默认值 |
resource |
资源名,必填 |
|
count |
限流阈值,必填 |
|
grade |
限流模式 |
QPS 模式 |
durationInSec |
统计窗口时间长度(单位为秒),1.6.0 版本开始支持 |
1s |
controlBehavior |
流控效果(支持快速失败和匀速排队模式),1.6.0 版本开始支持 |
快速失败 |
maxQueueingTimeMs |
最大排队等待时长(仅在匀速排队模式生效),1.6.0 版本开始支持 |
0ms |
paramIdx |
热点参数的索引,必填,对应 SphU.entry(xxx, args) 中的参数索引位置 |
|
paramFlowItemList |
参数例外项,可以针对指定的参数值单独设置限流阈值,不受前面 count 阈值的限制。仅支持基本类型 |
|
clusterMode |
是否是集群参数流控规则 |
false |
clusterConfig |
集群流控相关配置 |
|
代码配置示例
1 2 3 4 5 6 7 8 9 10
| ParamFlowRule rule = new ParamFlowRule(resourceName) .setParamIdx(0) .setCount(5);
ParamFlowItem item = new ParamFlowItem().setObject(String.valueOf(PARAM_B)) .setClassType(int.class.getName()) .setCount(10); rule.setParamFlowItemList(Collections.singletonList(item));
ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
|
系统规则
配置
参数
Field |
说明 |
默认值 |
highestSystemLoad |
最大的 load1 |
-1 (不生效) |
avgRt |
所有入口流量的平均响应时间 |
-1 (不生效) |
maxThread |
入口流量的最大并发数 |
-1 (不生效) |
qps |
所有入口资源的 QPS |
-1 (不生效) |
代码配置示例
1 2 3 4 5 6 7
| private void initSystemRule() { List<SystemRule> rules = new ArrayList<>(); SystemRule rule = new SystemRule(); rule.setHighestSystemLoad(10); rules.add(rule); SystemRuleManager.loadRules(rules); }
|
授权规则
配置
参数
Field |
说明 |
默认值 |
resource |
资源名,即限流规则的作用对象 |
- |
limitApp |
对应的黑名单/白名单,不同 origin 用 , 分隔,如 appA,appB |
default,代表不区分调用来源 |
strategy |
限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名单模式 |
AUTHORITY_WHITE |
5.3 代码配置示例
1 2 3 4 5
| AuthorityRule rule = new AuthorityRule(); rule.setResource("test"); rule.setStrategy(RuleConstant.AUTHORITY_WHITE); rule.setLimitApp("appA,appB"); AuthorityRuleManager.loadRules(Collections.singletonList(rule));
|
概述
前面我们介绍了sentinel控制台以及客户端的启动,并且实现了在控制台添加流控以及熔断规则,并且将规则发送给客户端。但是,将客户端重启,我们会发现,我们之前设置的规则将会丢失,这是因为客户端将规则保存在内存中,并没有将其持久化,因而,这样并不适用于生产环境。
Sentinel数据源
Sentinel的数据源可以通过多种方式加载:json、xml文件;zookeeper;apollo;nacos;并且可以多种数据源同时使用。
扩展方式
动态数据源:DataSource`扩展常见的实现方式
拉模式
客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件,甚至是 VCS 等。这样做的方式是简单,缺点是无法及时获取变更;
推模式
规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。
原理简述
控制台推送规则:
要具体了解可以查看官网的介绍:动态规则扩展
在生产环境下推荐使用推模式, 这需要设置客户端,并且需要修改Sentinel控制台的源码。
我们推荐通过控制台设置规则后将规则推送到统一的规则中心,客户端实现 ReadableDataSource
接口端监听规则中心实时获取变更,流程如下:
各个模式的对比
一般来说,规则的推送有下面三种模式:
推送模式 |
说明 |
优点 |
缺点 |
原始模式 |
API 将规则推送至客户端并直接更新到内存中,扩展写数据源(WritableDataSource ) |
简单,无任何依赖 |
不保证一致性;规则保存在内存中,重启即消失。严重不建议用于生产环境 |
Pull 模式 |
扩展写数据源(WritableDataSource ), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件 等 |
简单,无任何依赖;规则持久化 |
不保证一致性;实时性不保证,拉取过于频繁也可能会有性能问题。 |
Push 模式 |
扩展读数据源(ReadableDataSource ),规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。生产环境下一般采用 push 模式的数据源。 |
规则持久化;一致性;快速 |
引入第三方依赖 |
限流规则推送Nacos
Sentinel客户端配置
引入POM依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <version>2.2.1.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> <version>1.7.2</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.70</version> </dependency>
|
修改配置文件
默认sentinel控制台推送到nacos的dataId定义为: ${spring.application.name}-flow-rules
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| server.port=8083
spring.application.name=order-server
spring.cloud.nacos.server-addr=192.168.64.128:8848
management.endpoints.web.exposure.include=* management.endpoint.health.show-details=ALWAYS
spring.cloud.sentinel.filter.enabled=false
spring.cloud.sentinel.transport.port=8731
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8080
spring.cloud.sentinel.datasource.ds.nacos.server-addr=${spring.cloud.nacos.server-addr} spring.cloud.sentinel.datasource.ds.nacos.dataId=${spring.application.name}-flow-rules spring.cloud.sentinel.datasource.ds.nacos.groupId=SENTINEL_GROUP
spring.cloud.sentinel.datasource.ds3.nacos.data-type=json
spring.cloud.sentinel.datasource.ds.nacos.rule-type=flow
|
添加Nacos数据源配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| @Configuration public class DataSourceInitFunc {
Logger logger = LoggerFactory.getLogger(DataSourceInitFunc.class);
@Autowired private SentinelProperties sentinelProperties;
@Bean public DataSourceInitFunc init() throws Exception {
logger.info("[NacosSource初始化,从Nacos中获取熔断规则]");
sentinelProperties.getDatasource().entrySet().stream().filter(map -> { return map.getValue().getNacos() != null; }).forEach(map -> { NacosDataSourceProperties nacos = map.getValue().getNacos(); if (nacos.getDataId().contains("flow")) { ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(nacos.getServerAddr(), nacos.getGroupId(), nacos.getDataId(), source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() { })); FlowRuleManager.register2Property(flowRuleDataSource.getProperty()); }
if (nacos.getDataId().contains("degrade")) { ReadableDataSource<String, List<DegradeRule>> degradeRuleDataSource = new NacosDataSource<>(nacos.getServerAddr(), nacos.getGroupId(), nacos.getDataId(), source -> JSON.parseObject(source, new TypeReference<List<DegradeRule>>() { })); DegradeRuleManager.register2Property(degradeRuleDataSource.getProperty()); }
}); return new DataSourceInitFunc(); } }
|
Nacos中添加熔断规则
根据配置文件配置的 id是 应用名-flow-rules
规则文件可以是json文件;也可以是text文件
规则是将 createOrder 的资源进行限流 使用QPS 模式 限流阈值 1
1 2 3 4 5 6 7 8 9 10 11
| [ { "resource": "createOrder", "limitApp": "default", "grade": 1, "count": 1, "strategy": 0, "controlBehavior": 0, "clusterMode": false } ]
|
具体参数参考 规则参数
测试
访问 http://localhost:8083/order/createorder/1 进行测试
这样会自动从配置中心读取配置信息不需要每次重启都需要配置了
控制台源码修改
原始的客户端是将熔断规则保存在内存中,一旦客户端重启,熔断规则将全部丢失。上面介绍了Sentinel客户端配置Nacos数据源,熔断规则持久化到Nacos中。客户端启动时,从Nacos数据源中加载熔断规则,并且会监听Nacos数据源中规则的变化,一旦Nacos中的规则改变,Sentinel客户端也会随之改变。从而,重启后,客户端之前的熔断规则不会丢失,实现熔断规则持久化。
但是Nacos中的规则需要我们手动添加,这样很不方便。通常,我们是在Sentinel控制台添加熔断规则,所以,我们接下来需要做的是,Sentinel控制台 将熔断规则推送到Nacos数据源。
控制台改造主要是为规则实现
- DynamicRuleProvider:从Nacos上读取配置
- DynamicRulePublisher:将规则推送到Nacos上
下载控制台源码
下载Sentinel控制台:https://github.com/alibaba/Sentinel/releases
修改POM文件
将sentinel-datasource-nacos的 <scope>test</scope>
这一行注释掉
1 2 3 4 5 6
| <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> <scope>test</scope> </dependency>
|
如下效果
1 2 3 4 5 6
| <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
|
相关类修改
找到 sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos
目录,将整个目录拷贝到 sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/nacos
修改NacosConfig类
修改NacosConfig类,修改NacosIP地址
FlowControllerV1修改
自动注入provider和publisher
1 2 3 4 5 6 7
| @Autowired @Qualifier("flowRuleNacosProvider") private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired @Qualifier("flowRuleNacosPublisher") private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
|
修改rules接口,读取nacos中的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @GetMapping("/rules") @AuthAction(PrivilegeType.READ_RULE) public Result<List<FlowRuleEntity>> apiQueryMachineRules(@RequestParam String app) {
if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } try { List<FlowRuleEntity> rules = ruleProvider.getRules(app); if (rules != null && !rules.isEmpty()) { for (FlowRuleEntity entity : rules) { entity.setApp(app); if (entity.getClusterConfig() != null && entity.getClusterConfig().getFlowId() != null) { entity.setId(entity.getClusterConfig().getFlowId()); } } } rules = repository.saveAll(rules); return Result.ofSuccess(rules); } catch (Throwable throwable) { logger.error("Error when querying flow rules", throwable); return Result.ofThrowable(-1, throwable); } }
|
该类最后的publishRules方法修改如下
1 2 3 4 5 6 7 8 9 10 11
| private CompletableFuture<Void> publishRules(String app, String ip, Integer port) { List<FlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); try { rulePublisher.publish(app, rules); logger.info("添加限流规则成功....."); } catch (Exception e) { e.printStackTrace(); logger.info("添加限流规则失败....."); } return sentinelApiClient.setFlowRuleOfMachineAsync(app, ip, port, rules); }
|
测试
访问http://localhost:8080/可以进入到控制台界面,但是界面上没有任何的客户端
请求一次http://localhost:8083/order/createorder/1,等一会儿,刷新一下就可以看到客户端了
添加流控规则
并刷新一下就可以看到我们添加的规则了
在nacos配置中心查看
我们发现配置已经被推送到了nacos
重启客户端进行测试
重启客户端后发现我们的规则是生效的,并且修改规则后重启客户端规则也是生效的
访问 http://localhost:8083/order/createorder/1 进行测试
降级规则推送Nacos
控制台源码修改
是在前面已经完成限流规则推送的控制台代码基础上继续修改的
相关类修改
修改NacosConfigUtil
添加降级规则后缀
1
| public static final String DEGRADE_DATA_ID_POSTFIX = "-degrade-rules";
|
添加降级规则推送类
复制限流规则并进行重命名
修改DegradeRuleNacosProvider类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Component("degradeRuleNacosProvider") public class DegradeRuleNacosProvider implements DynamicRuleProvider<List<DegradeRuleEntity>> {
@Autowired private ConfigService configService; @Autowired private Converter<String, List<DegradeRuleEntity>> converter;
@Override public List<DegradeRuleEntity> getRules(String appName) throws Exception { String rules = configService.getConfig(appName + NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX, NacosConfigUtil.GROUP_ID, 3000); if (StringUtil.isEmpty(rules)) { return new ArrayList<>(); } return converter.convert(rules); } }
|
修改DegradeRuleNacosPublisher类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Component("degradeRuleNacosPublisher") public class DegradeRuleNacosPublisher implements DynamicRulePublisher<List<DegradeRuleEntity>> {
@Autowired private ConfigService configService; @Autowired private Converter<List<DegradeRuleEntity>, String> converter;
@Override public void publish(String app, List<DegradeRuleEntity> rules) throws Exception { AssertUtil.notEmpty(app, "app name cannot be empty"); if (rules == null) { return; } configService.publishConfig(app + NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX, NacosConfigUtil.GROUP_ID, converter.convert(rules)); } }
|
注入降级规则转换器
规则转换器是负责将降级规则对象转换为json字符串
在 NacosConfig 类中添加注入两个转换器
1 2 3 4 5 6 7 8 9 10
| @Bean public Converter<List<DegradeRuleEntity>, String> degradeRuleEntityEncoder() { return JSON::toJSONString; }
@Bean public Converter<String, List<DegradeRuleEntity>> degradeRuleEntityDecoder() { return s -> JSON.parseArray(s, DegradeRuleEntity.class);
}
|
修改DegradeController
使用NacosPublisher推送规则,代替之前的直接推送到Sentinel客户端的方式
自动注入provider和publisher
1 2 3 4 5 6 7
| @Autowired @Qualifier("degradeRuleNacosProvider") private DynamicRuleProvider<List<DegradeRuleEntity>> provider;
@Autowired @Qualifier("degradeRuleNacosPublisher") private DynamicRulePublisher<List<DegradeRuleEntity>> publisher;
|
修改DegradeController的publishRules方法
1 2 3 4 5 6 7 8 9 10 11
| private boolean publishRules(String app, String ip, Integer port) { List<DegradeRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); try { publisher.publish(app, rules); logger.info("推送限流规则到Nacos数据源成功....."); } catch (Exception e) { e.printStackTrace(); logger.info("推送限流规则到Nacos数据源失败....."); } return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules); }
|
编译运行
到这里控制台代码端的代码修改完成了,编译运行没有报错就可以了
测试
启动Nacos控制台新增降级规则
我们发现sentinel已经将降级规则同步了nacos中,dataId是:order-server-degrade-rules
降级内容如下
Sentinel客户端配置
引入POM依赖(省略)
修改配置文件
默认sentinel控制台推送到nacos的dataId定义为: ${spring.application.name}-degrade-rules
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| server.port=8083
spring.application.name=order-server
spring.cloud.nacos.server-addr=192.168.64.128:8848
management.endpoints.web.exposure.include=* management.endpoint.health.show-details=ALWAYS
spring.cloud.sentinel.filter.enabled=false
spring.cloud.sentinel.transport.port=8731
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8080
spring.cloud.sentinel.datasource.ds.nacos.server-addr=${spring.cloud.nacos.server-addr} spring.cloud.sentinel.datasource.ds.nacos.dataId=${spring.application.name}-degrade-rules spring.cloud.sentinel.datasource.ds.nacos.groupId=SENTINEL_GROUP
spring.cloud.sentinel.datasource.ds3.nacos.data-type=json
spring.cloud.sentinel.datasource.ds3.nacos.rule-type=degrade
|
添加Nacos数据源配置类(省略)
测试
启动客户端以及控制台
访问 http://localhost:8083/actuator/sentinel
可以查看到客户端的启动时从Nacos中加载的所有规则
使用postman测试
访问 http://localhost:8083/order/createorder/1