Sentinel的持久化
官网地址:https://github.com/alibaba/Sentinel/wiki/%E5%9C%A8%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Sentinel
一、规则管理及推送
Sentinel-dashboard配置的规则是基于内存的,在微服务以及控制台重启的时候就清空了;
一般来说,规则的推送有下面三种模式:
1、原始模式
如果不做任何修改,Dashboard 的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中;
优缺点:这种做法的好处是简单,无依赖;坏处是应用重启规则就会消失,仅用于简单测试,不能用于生产环境
2、Pull模式
pull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。使用时需要在客户端注册数据源:将对应的读数据源注册至对应的 RuleManager,将写数据源注册至 transport 的 WritableDataSourceRegistry 中。
首先 Sentinel 控制台通过 API 将规则推送至客户端并更新到内存中,接着注册的写数据源会将新的规则保存到本地的文件中。使用 pull 模式的数据源时一般不需要对 Sentinel 控制台进行改造。
这种实现方法好处是简单,不引入新的依赖,坏处是无法保证监控数据的一致性。
(1)通过SPI扩展机制进行扩展,在resources目录下创建 META-INF/services/com.alibaba.csp.sentinel.init.InitFunc 文件;我们写一个拉模式的实现类 com.yufeng.persistence.PullModeByFileDataSource ,并将这个类的全类名写入到文件中。
(2)实现的类 PullModeByFileDataSource 类,如下:
1 @Slf4j 2 public class PullModeByFileDataSource implements InitFunc { 3 4 @Override 5 public void init() { 6 log.info("time:{}读取配置", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"))); 7 8 // 1. 创建文件夹 9 try { 10 this.mkdirIfNotExits(PersistenceRuleConstant.storePath); 11 12 // 2. 创建文件 13 this.createFileIfNotExits(PersistenceRuleConstant.rulesMap); 14 15 // 3.处理流控规则 16 this.dealFlowRules(); 17 18 // 4.处理降级规则 19 this.dealDegradeRules(); 20 21 // 5.处理系统规则 22 this.dealSystemRules(); 23 24 // 6.处理热点参数规则 25 this.dealParamFlowRules(); 26 27 // 7.处理授权规则 28 this.dealAuthRules(); 29 } catch (IOException e) { 30 log.error("错误原因:{}",e); 31 } 32 } 33 34 /** 35 * 处理流控规则 36 */ 37 private void dealFlowRules() throws FileNotFoundException { 38 String ruleFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.FLOW_RULE_PATH).toString(); 39 40 // 1. 创建流控规则的可读数据源 41 ReadableDataSource<String, List<FlowRule>> flowRuleRds = new FileRefreshableDataSource<> ( 42 ruleFilePath, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}) 43 ); 44 45 // 2. 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存 46 FlowRuleManager.register2Property(flowRuleRds.getProperty()); 47 48 WritableDataSource<List<FlowRule>> flowRuleWds = new FileWritableDataSource<List<FlowRule>>( 49 ruleFilePath, JSON::toJSONString); 50 51 // 3. 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中. 52 // 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中. 53 WritableDataSourceRegistry.registerFlowDataSource(flowRuleWds); 54 } 55 56 /** 57 * 降级规则 58 */ 59 private void dealDegradeRules() throws FileNotFoundException { 60 String degradeRuleFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.DEGRAGE_RULE_PATH).toString(); 61 62 // 1. 创建流控规则的可读数据源 63 ReadableDataSource<String, List<DegradeRule>> degradeRuleRds = new FileRefreshableDataSource<>( 64 degradeRuleFilePath, source -> JSON.parseObject(source, new TypeReference<List<DegradeRule>>() {}) 65 ); 66 67 // 2.将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存 68 DegradeRuleManager.register2Property(degradeRuleRds.getProperty()); 69 70 WritableDataSource<List<DegradeRule>> degradeRuleWds = new FileWritableDataSource<>( 71 degradeRuleFilePath, JSON::toJSONString 72 ); 73 74 // 3. 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中. 75 // 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中. 76 WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWds); 77 } 78 79 /** 80 * 处理系统规则 81 */ 82 private void dealSystemRules() throws FileNotFoundException { 83 String systemRuleFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.SYSTEM_RULE_PATH).toString(); 84 85 // 1.创建流控规则的可读数据源 86 ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>( 87 systemRuleFilePath, source -> JSON.parseObject(source, new TypeReference<List<SystemRule>>() {}) 88 ); 89 90 // 2.将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存 91 SystemRuleManager.register2Property(systemRuleRDS.getProperty()); 92 93 WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>( 94 systemRuleFilePath, JSON::toJSONString 95 ); 96 97 // 3.将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中. 98 // 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中. 99 WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS); 100 } 101 102 /** 103 * 热点参数规则 104 */ 105 private void dealParamFlowRules() throws FileNotFoundException { 106 String paramFlowRuleFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.HOT_PARAM_RULE).toString(); 107 108 // 1. 创建流控规则的可读数据源 109 ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>( 110 paramFlowRuleFilePath, source -> JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {}) 111 ); 112 113 // 2. 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存 114 ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty()); 115 116 WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>( 117 paramFlowRuleFilePath, JSON::toJSONString 118 ); 119 120 // 3. 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中. 121 // 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中. 122 ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS); 123 } 124 125 /** 126 * 方法实现说明:授权规则 127 * @author:smlz 128 * @return: 129 * @exception: 130 * @date:2019/11/29 13:56 131 */ 132 private void dealAuthRules() throws FileNotFoundException { 133 String authFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.AUTH_RULE_PATH).toString(); 134 135 // 1.创建流控规则的可读数据源 136 ReadableDataSource<String, List<AuthorityRule>> authRuleRDS = new FileRefreshableDataSource<>( 137 authFilePath, source -> JSON.parseObject(source, new TypeReference<List<AuthorityRule>>() {}) 138 ); 139 140 // 2.将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存 141 AuthorityRuleManager.register2Property(authRuleRDS.getProperty()); 142 143 WritableDataSource<List<AuthorityRule>> authRuleWDS = new FileWritableDataSource<>( 144 authFilePath, JSON::toJSONString 145 ); 146 147 // 3.将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中. 148 // 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中. 149 WritableDataSourceRegistry.registerAuthorityDataSource(authRuleWDS); 150 } 151 152 /** 153 * 创建文件目录 154 */ 155 private void mkdirIfNotExits(String filePath) throws IOException { 156 File file = new File(filePath); 157 if(!file.exists()) { 158 log.info("创建Sentinel规则目录:{}",filePath); 159 file.mkdirs(); 160 } 161 } 162 163 /** 164 * 创建规则文件 165 */ 166 private void createFileIfNotExits(Map<String,String> ruleFileMap) throws IOException { 167 for (Map.Entry entry : ruleFileMap.entrySet()) { 168 String ruleFilePath = PersistenceRuleConstant.rulesMap.get(entry.getKey()).toString(); 169 File ruleFile = new File(ruleFilePath); 170 if(!ruleFile.exists()) { 171 log.info("创建Sentinel 规则文件:{}",ruleFile); 172 ruleFile.createNewFile(); 173 } 174 } 175 } 176 }
(3)常量类的定义
1 public class PersistenceRuleConstant { 2 3 /** 4 * 存储文件路径 5 */ 6 public static final String storePath = System.getProperty("user.home")+"\\sentinel\\rules\\"; 7 8 /** 9 * 各种存储sentinel规则映射map 10 */ 11 public static final Map rulesMap = new HashMap<String,String>(); 12 13 //流控规则文件 14 public static final String FLOW_RULE_PATH = "flowRulePath"; 15 16 //降级规则文件 17 public static final String DEGRAGE_RULE_PATH = "degradeRulePath"; 18 19 //授权规则文件 20 public static final String AUTH_RULE_PATH = "authRulePath"; 21 22 //系统规则文件 23 public static final String SYSTEM_RULE_PATH = "systemRulePath"; 24 25 //热点参数文件 26 public static final String HOT_PARAM_RULE = "hotParamRulePath"; 27 28 static { 29 rulesMap.put(FLOW_RULE_PATH,storePath + "flowRule.json"); 30 rulesMap.put(DEGRAGE_RULE_PATH,storePath + "degradeRule.json"); 31 rulesMap.put(SYSTEM_RULE_PATH,storePath + "systemRule.json"); 32 rulesMap.put(AUTH_RULE_PATH,storePath + "authRule.json"); 33 rulesMap.put(HOT_PARAM_RULE,storePath + "hotParamRule.json"); 34 } 35 }
3、Push模式
生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(ZooKeeper, Nacos, Apollo等等),推送的操作不应由 Sentinel 客户端进行,而应该经控制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本地。因此推送规则正确做法应该是 配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel,而不是经 Sentinel 数据源推送至配置中心。这样的流程就非常清晰了:
原理简述
(1)控制台推送规则:
- 将规则推送到Nacos或其他远程配置中心;
- Sentinel客户端链接Nacos,获取规则配置;并监听Nacos配置变化,如发生变化,就更新本地缓存(从而让本地缓存总是和Nacos一致);
(2)控制台监听Nacos配置变化,如发生变化就更新本地缓存(从而让控制台本地缓存总是和Nacos一致)
改造方案