这里讲下基于本地文件的Pull模式持久化。
从官网的说明https://github.com/alibaba/Sentinel/wiki/在生产环境中使用-Sentinel#Pull模式
有如下一张图,总觉得例子欠缺些什么??
琢磨一下,发现原来无论官方的例子,还是网友的例子都没有结合Sentinel讲解规则的Pull。现在开始操作,按照官方的步骤开始操作:https://github.com/alibaba/Sentinel/wiki/动态规则扩展
在微服务的pom.xml文件引入<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-extension</artifactId> <version>x.y.z</version></dependency>
不知道哪里出了问题,到最后也没有搞通?而且还有如下疑问
微服务到底连接哪个Sentinel服务?如果没有配置,可能可以连接默认的Sentinel服务
如果Sentinel服务的ip和port变了呢?如何配置?也没有找到具体说明?
带着这两个疑问开始学习之旅。因为基于springboot和springcloud讲解,所有Spring Cloud Alibaba这么好的技术,为什么不用呢?
0x01:新建项目olive-pull-sentinel-datasource
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.sentinel</groupId> <artifactId>olive-pull-sentinel-datasource</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <name>olive-pull-sentinel-datasource</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <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-parameter-flow-control</artifactId> <version>1.7.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version> 1.2.68</version> </dependency> </dependencies></project>
常量类,主要是定义规则文件的目录和名字package com.sentinel.olive.file;import java.util.HashMap;import java.util.Map;public class PersistenceRuleConstant { /** * 存储文件路径 */ public static final String storePath = System.getProperty("user.home")+"\\sentinel\\rules\"; /** * 各种存储sentinel规则映射map */ public static final Map<String,String> rulesMap = new HashMap<String,String>(); //流控规则文件 public static final String FLOW_RULE_PATH = "flowRulePath"; //降级规则文件 public static final String DEGRAGE_RULE_PATH = "degradeRulePath"; //授权规则文件 public static final String AUTH_RULE_PATH = "authRulePath"; //系统规则文件 public static final String SYSTEM_RULE_PATH = "systemRulePath"; //热点参数文件 public static final String HOT_PARAM_RULE = "hotParamRulePath"; static { rulesMap.put(FLOW_RULE_PATH,storePath+"flowRule.json"); rulesMap.put(DEGRAGE_RULE_PATH,storePath+"degradeRule.json"); rulesMap.put(SYSTEM_RULE_PATH,storePath+"systemRule.json"); rulesMap.put(AUTH_RULE_PATH,storePath+"authRule.json"); rulesMap.put(HOT_PARAM_RULE,storePath+"hotParamRule.json"); }}
文件操作类,如果规则文件不存在就创建对应的目录和对应的规则文件package com.sentinel.olive.file;import java.io.File;import java.io.IOException;import java.util.Iterator;import java.util.Map;import java.util.Set;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class RuleFileUtils { private static final Logger logger = LoggerFactory.getLogger(RuleFileUtils.class); /** * 方法实现说明:若路径不存在就创建路径 * @param filePath:文件存储路径 */ public static void mkdirIfNotExits(String filePath) throws IOException { File file = new File(filePath); if(!file.exists()) { logger.info("创建Sentinel规则目录:{}",filePath); file.mkdirs(); } } /** * 方法实现说明:若文件不存在就创建路径 * @param ruleFileMap 规则存储文件 */ public static void createFileIfNotExits(Map<String,String> ruleFileMap) throws IOException { Set<String> ruleFilePathSet = ruleFileMap.keySet(); Iterator<String> ruleFilePathIter = ruleFilePathSet.iterator(); while (ruleFilePathIter.hasNext()) { String ruleFilePathKey = ruleFilePathIter.next(); String ruleFilePath = PersistenceRuleConstant.rulesMap.get(ruleFilePathKey).toString(); File ruleFile = new File(ruleFilePath); if(ruleFile.exists()) { logger.info("创建Sentinel 规则文件:{}",ruleFile); ruleFile.createNewFile(); } } }}
规则的编码和解码操作类package com.sentinel.olive.file;import java.util.List;import com.alibaba.csp.sentinel.datasource.Converter;import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;import com.alibaba.csp.sentinel.slots.system.SystemRule;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.TypeReference;public class RuleListParserUtils { /** * 流控列表解析器 */ public static final Converter<String, List<FlowRule>> flowRuleListParser = new Converter<String, List<FlowRule>>() { @Override public List<FlowRule> convert(String source) { return JSON.parseObject(source, new TypeReference<List<FlowRule>>() { }); } }; /** * 流控列表 编码器 */ public static final Converter<List<FlowRule>, String> flowRuleEnCoding = new Converter<List<FlowRule>, String>() { @Override public String convert(List<FlowRule> source) { return JSON.toJSONString(source); } }; public static final Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(source, new TypeReference<List<DegradeRule>>() { }); public static final Converter<List<DegradeRule>, String> degradeRuleEnCoding = new Converter<List<DegradeRule>, String>() { @Override public String convert(List<DegradeRule> source) { return JSON.toJSONString(source); } }; public static final Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(source, new TypeReference<List<SystemRule>>() { }); public static final Converter<List<SystemRule>, String> systemRuleEnCoding = new Converter<List<SystemRule>, String>() { @Override public String convert(List<SystemRule> source) { return JSON.toJSONString(source); } }; public static final Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON .parseObject(source, new TypeReference<List<AuthorityRule>>() { }); public static final Converter<List<AuthorityRule>, String> authorityRuleEnCoding = new Converter<List<AuthorityRule>, String>() { @Override public String convert(List<AuthorityRule> source) { return JSON.toJSONString(source); } }; public static final Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON .parseObject(source, new TypeReference<List<ParamFlowRule>>() { });// public static final Converter<List<ParamFlowRule>, String> paramFlowRuleEnCoding = new Converter<List<ParamFlowRule>, String>() {// @Override// public String convert(List<ParamFlowRule> source) {// return JSON.toJSONString(source);// }// }; public static final Converter<List<ParamFlowRule>, String> paramFlowRuleEnCoding = source -> encodeJson(source); private static <T> String encodeJson(T t) { return JSON.toJSONString(t); }}
具体pull模式操作类package com.sentinel.olive.file;import java.io.FileNotFoundException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.List;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource;import com.alibaba.csp.sentinel.datasource.FileWritableDataSource;import com.alibaba.csp.sentinel.datasource.ReadableDataSource;import com.alibaba.csp.sentinel.datasource.WritableDataSource;import com.alibaba.csp.sentinel.init.InitFunc;import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;import com.alibaba.csp.sentinel.slots.system.SystemRule;import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;public class PullModeLocalFileDataSource implements InitFunc { private static final Logger logger = LoggerFactory.getLogger(PullModeLocalFileDataSource.class); private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); @Override public void init() throws Exception { logger.info("time:{}读取配置", sdf.format(new Date())); try { // 创建文件存储目录(若路径不存在就创建路径) RuleFileUtils.mkdirIfNotExits(PersistenceRuleConstant.storePath); // 创建规则文件() RuleFileUtils.createFileIfNotExits(PersistenceRuleConstant.rulesMap); // 处理流控规则逻辑 dealFlowRules(); // 处理降级规则 dealDegradeRules(); // 处理系统规则 dealSystemRules(); // 热点参数规则 dealParamFlowRules(); // 授权规则 dealAuthRules(); } catch (Exception e) { logger.error("错误原因:{}", e); } } /** * 方法实现说明:处理流控规则逻辑 */ private void dealFlowRules() throws FileNotFoundException { String ruleFilePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.FLOW_RULE_PATH).toString(); // 创建流控规则的可读数据源 ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(ruleFilePath, RuleListParserUtils.flowRuleListParser); // 将可读数据源注册至FlowRuleManager 这样当规则文件发生变化时,就会更新规则到内存 FlowRuleManager.register2Property(flowRuleRDS.getProperty()); WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<List<FlowRule>>(ruleFilePath, RuleListParserUtils.flowRuleEnCoding); // 将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中. // 这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中. WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS); } // 处理降级规则 private void dealDegradeRules() throws FileNotFoundException { String degradeRulePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.DEGRAGE_RULE_PATH).toString(); // 降级规则 ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(degradeRulePath, RuleListParserUtils.degradeRuleListParser); DegradeRuleManager.register2Property(degradeRuleRDS.getProperty()); WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(degradeRulePath, RuleListParserUtils.degradeRuleEnCoding); WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS); } // 处理系统规则 private void dealSystemRules() throws FileNotFoundException { String systemRulePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.SYSTEM_RULE_PATH).toString(); // 系统规则 ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(systemRulePath, RuleListParserUtils.systemRuleListParser); SystemRuleManager.register2Property(systemRuleRDS.getProperty()); WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(systemRulePath, RuleListParserUtils.systemRuleEnCoding); WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS); } // 热点参数规则 private void dealParamFlowRules() throws FileNotFoundException { String paramFlowRulePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.HOT_PARAM_RULE).toString(); // 热点参数规则 ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>( paramFlowRulePath, RuleListParserUtils.paramFlowRuleListParser); ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty()); WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(paramFlowRulePath, RuleListParserUtils.paramFlowRuleEnCoding); ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS); } private void dealAuthRules() throws FileNotFoundException { String authFlowRulePath = PersistenceRuleConstant.rulesMap.get(PersistenceRuleConstant.AUTH_RULE_PATH).toString(); // 授权规则 ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(authFlowRulePath, RuleListParserUtils.authorityRuleListParser); AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty()); WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(authFlowRulePath, RuleListParserUtils.authorityRuleEnCoding); WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS); }}
编写了以上代码就可以,通过SPI扩展机制进行扩展,在微服务工程 olive-pull-sentinel-datasource的resources目录下创建META-INF/services目录,并新建文件名为com.alibaba.csp.sentinel.init.InitFunc文件。内容是PullModeLocalFileDataSource类全路径类名
0x02:配置文件application.yml
看到这张图总有解决了以上的疑问了,提供了配置项,配置Sentinel服务对应的ip和端口
0x03:创建测试控制器和springboot启动类
测试控制器package com.sentinel.olive.controller;import java.util.HashMap;import java.util.Map;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class UserController { @GetMapping("/getUser") public Map<String, Object> getUser() { Map<String, Object> result = new HashMap<>(); result.put("code", "000000"); result.put("message", "ok"); return result; }}
springboot启动类package com.sentinel.olive;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}
0x04:启动并测试验证
启动服务
sentinel-dashboard服务
olive-pull-sentinel-datasource微服务
验证
sentinel-dashboard创建规则,olive-pull-sentinel-datasource微服务检测到sentinel-dashboard变化
修改json规则文件的规则,sentinel-dashboard检测到json规则编码
访问微服务的http://localhost:8866/getUser接口
访问sentinel-dashboard服务:http://127.0.0.1:8080/
创建流控规则
olive-pull-sentinel-datasource检测到流控规则的变化并产生flowRule.json文件
修改流控规则文件json
sentinel-dashboard查看规则的变化