本文实现基于拉模式的 Alibaba Sentinel 规则持久化。
一、拉模式架构

TIPS 图片来自官方。 引用自
https://github.com/alibaba/Sentinel/wiki / 在生产环境中使用 - Sentinel
二、原理简述
- FileRefreshableDataSource 定时从指定文件中读取规则 JSON 文件【图中的本地文件】,如果发现文件发生变化,就更新规则缓存。
- FileWritableDataSource 接收控制台规则推送,并根据配置,修改规则 JSON 文件【图中的本地文件】。
三、编写
修改 Spring Cloud Alibaba 微服务。
3.1 加依赖
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-extension</artifactId></dependency>
3.2 写代码
/*** 拉模式规则持久化** @author itmuch.com*/public class FileDataSourceInit implements InitFunc {@Overridepublic void init() throws Exception {// TIPS: 如果你对这个路径不喜欢,可修改为你喜欢的路径String ruleDir = System.getProperty("user.home") + "/sentinel/rules";String flowRulePath = ruleDir + "/flow-rule.json";String degradeRulePath = ruleDir + "/degrade-rule.json";String systemRulePath = ruleDir + "/system-rule.json";String authorityRulePath = ruleDir + "/authority-rule.json";String paramFlowRulePath = ruleDir + "/param-flow-rule.json";this.mkdirIfNotExits(ruleDir);this.createFileIfNotExits(flowRulePath);this.createFileIfNotExits(degradeRulePath);this.createFileIfNotExits(systemRulePath);this.createFileIfNotExits(authorityRulePath);this.createFileIfNotExits(paramFlowRulePath);// 流控规则ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(flowRulePath,flowRuleListParser);// 将可读数据源注册至FlowRuleManager// 这样当规则文件发生变化时,就会更新规则到内存FlowRuleManager.register2Property(flowRuleRDS.getProperty());WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(flowRulePath,this::encodeJson);// 将可写数据源注册至transport模块的WritableDataSourceRegistry中// 这样收到控制台推送的规则时,Sentinel会先更新到内存,然后将规则写入到文件中WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);// 降级规则ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(degradeRulePath,degradeRuleListParser);DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(degradeRulePath,this::encodeJson);WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);// 系统规则ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(systemRulePath,systemRuleListParser);SystemRuleManager.register2Property(systemRuleRDS.getProperty());WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(systemRulePath,this::encodeJson);WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);// 授权规则ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(authorityRulePath,authorityRuleListParser);AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(authorityRulePath,this::encodeJson);WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);// 热点参数规则ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(paramFlowRulePath,paramFlowRuleListParser);ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(paramFlowRulePath,this::encodeJson);ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);}private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {});private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(source,new TypeReference<List<DegradeRule>>() {});private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(source,new TypeReference<List<SystemRule>>() {});private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(source,new TypeReference<List<AuthorityRule>>() {});private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(source,new TypeReference<List<ParamFlowRule>>() {});private void mkdirIfNotExits(String filePath) throws IOException {File file = new File(filePath);if (!file.exists()) {file.mkdirs();}}private void createFileIfNotExits(String filePath) throws IOException {File file = new File(filePath);if (!file.exists()) {file.createNewFile();}}private <T> String encodeJson(T t) {return JSON.toJSONString(t);}}
3.3 配置
在项目的 resources/META-INF/services 目录下创建文件,名为 com.alibaba.csp.sentinel.init.InitFunc ,内容为:
# 改成上面FileDataSourceInit的包名类名全路径即可。com.itmuch.contentcenter.FileDataSourceInit
四、优缺点分析
- 优点
- 简单易懂
- 没有多余依赖(比如配置中心、缓存等)
- 缺点
- 由于规则是用 FileRefreshableDataSource 定时更新的,所以规则更新会有延迟。如果 FileRefreshableDataSource 定时时间过大,可能长时间延迟;如果 FileRefreshableDataSource 过小,又会影响性能;
- 规则存储在本地文件,如果有一天需要迁移微服务,那么需要把规则文件一起迁移,否则规则会丢失。
五、你可能会有的疑问
Spring Cloud Alibaba 不是提供了如下配置了吗?为什么要全部自己写呢?
spring.cloud.sentinel.datasource.ds1.file.file=classpath: degraderule.jsonspring.cloud.sentinel.datasource.ds1.file.rule-type=flow#spring.cloud.sentinel.datasource.ds1.file.file=classpath: flowrule.json#spring.cloud.sentinel.datasource.ds1.file.data-type=custom#spring.cloud.sentinel.datasource.ds1.file.converter-class=com.alibaba.cloud.examples.JsonFlowRuleListConverter#spring.cloud.sentinel.datasource.ds1.file.rule-type=flow
关于这个问题,可以看我提的 Issue:https://github.com/alibaba/spring-cloud-alibaba/issues/756
六、参考文档
https://github.com/alibaba/Sentinel/wiki / 在生产环境中使用 - Sentinel#pull 模式
