在Sentinel控制台对某个微服务的接口资源配置了流控、降级等规则后,若重启了该微服务,那么配置的相关规则就会丢失,由于Sentinel默认将规则存放在内存中。每次重启微服务都得从新配置规则显然是不合理的,因此咱们须要将配置好的规则进行持久化存储,而Sentinel提供了两种规则持久化模式:html
本小节先介绍一下拉模式(pull),该模式的架构图以下:java
由于须要读写本地文件,因此实现拉模式须要编写一些代码,首先在项目中添加以下依赖:node
<!-- Sentinel Datasource --> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-extension</artifactId> </dependency>
因为Sentinel有好几种规则,因此须要写的代码也有点多,具体代码以下示例:git
package com.zj.node.contentcenter.sentinel; import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler; import com.alibaba.csp.sentinel.datasource.*; 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; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import lombok.extern.slf4j.Slf4j; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.List; /** * 规则持久化 - 拉模式 * * @author 01 * @date 2019-08-02 **/ @Slf4j public class FileDataSourceInitial implements InitFunc { /** * 定义并实现各个规则对象的转换器,用于将json格式的数据转换为相应的Java对象 */ 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>>() { } ); @Override public void init() throws Exception { // 规则持久化文件所存放的路径及文件名,能够按需求自行修改 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); // 注册各个规则的可读写数据源 this.registerFlowRWDS(flowRulePath); this.registerDegradeRWDS(degradeRulePath); this.registerSystemRWDS(systemRulePath); this.registerAuthorityRWDS(authorityRulePath); this.registerParamRWDS(paramFlowRulePath); } /** * 注册流控规则的可读写数据源 */ private void registerFlowRWDS(String flowRulePath) throws FileNotFoundException { // 构建可读数据源,用于定时读取本地的json文件 ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>( flowRulePath, flowRuleListParser ); // 将可读数据源注册至FlowRuleManager,当文件里的规则内容发生变化时,就会更新到缓存里 FlowRuleManager.register2Property(flowRuleRDS.getProperty()); // 构建可写数据源 WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>( flowRulePath, this::toJson ); // 将可写数据源注册至transport模块的WritableDataSourceRegistry中 // 这样收到控制台推送的规则时,Sentinel会先更新到内存,而后将规则写入到文件中 WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS); } /** * 注册降级规则的可读写数据源 */ private void registerDegradeRWDS(String degradeRulePath) throws FileNotFoundException { ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>( degradeRulePath, degradeRuleListParser ); DegradeRuleManager.register2Property(degradeRuleRDS.getProperty()); WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>( degradeRulePath, this::toJson ); WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS); } /** * 注册系统规则的可读写数据源 */ private void registerSystemRWDS(String systemRulePath) throws FileNotFoundException { ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>( systemRulePath, systemRuleListParser ); SystemRuleManager.register2Property(systemRuleRDS.getProperty()); WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>( systemRulePath, this::toJson ); WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS); } /** * 注册受权规则的可读写数据源 */ private void registerAuthorityRWDS(String authorityRulePath) throws FileNotFoundException { ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>( authorityRulePath, authorityRuleListParser ); AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty()); WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>( authorityRulePath, this::toJson ); WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS); } /** * 注册热点参数规则的可读写数据源 */ private void registerParamRWDS(String paramFlowRulePath) throws FileNotFoundException { ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>( paramFlowRulePath, paramFlowRuleListParser ); ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty()); WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>( paramFlowRulePath, this::toJson ); ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS); } private void mkdirIfNotExits(String filePath) { File file = new File(filePath); if (!file.exists()) { boolean result = file.mkdirs(); log.info("建立目录: {} filePath: {}", result ? "成功" : "失败", filePath); } } private void createFileIfNotExits(String filePath) throws IOException { File file = new File(filePath); if (!file.exists()) { boolean result = file.createNewFile(); log.info("建立文件: {} filePath: {}", result ? "成功" : "失败", filePath); } } private <T> String toJson(T t) { return JSON.toJSONString(t); } }
这里有两个重要的API:github
FileRefreshableDataSource
定时从指定文件中读取规则JSON文件【上图中的本地文件】,若是发现文件发生变化,就更新规则缓存FileWritableDataSource
接收控制台规则推送,并根据配置,修改规则JSON文件【上图中的本地文件】编写完以上代码后,还须要在项目的 resources/META-INF/services
目录下建立一个文件,名为 com.alibaba.csp.sentinel.init.InitFunc
,以下图所示:web
而后编辑文件内容以下:spring
# 修改成上面FileDataSourceInitial的包名类名全路径 com.zj.node.contentcenter.sentinel.FileDataSourceInitial
完成以上步骤后,重启项目,此时就能够自行测试一下规则是否能持久化存储了。json
拉模式的优缺点:浏览器
若是有了解过规则持久化相关配置的小伙伴可能会有疑问,Spring Cloud Alibaba不是提供了以下配置了吗?为何要所有本身写呢?缓存
spring.cloud.sentinel.datasource.ds1.file.file=classpath: degraderule.json spring.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
关于这个问题,能够查看一下这个Issues
官方文档:
在上一小节中,咱们了解了规则持久化中拉模式的原理及使用方式,本小节将介绍在生产环境中更为经常使用的推模式(push)。
推模式的架构图以下:
使用推模式进行规则的持久化仍是稍微有些麻烦的,由于须要改动Sentinel控制台的源码,对控制台的改造主要是为了实现:
这里仅演示对流控规则的改造让其支持推模式的规则持久化,由于其余规则的改造过程也是相似的,稍微琢磨一下就能够了。首先须要下载Sentinel的源码包,我这里使用的是1.6.3版本:
下载并解压完成后,使用IDE打开sentinel-dashboard这个项目,以下:
第一步:修改该项目的pom.xml文件,找到以下依赖项:
<!-- for Nacos rule publisher sample --> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> <scope>test</scope> </dependency>
将<scope>test</scope>
一行注释掉,即修改成以下:
<!-- for Nacos rule publisher sample --> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> <!-- <scope>test</scope> --> </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
下,以下图所示:
拷贝完成后rule包结构以下图:
第三步:修改流控规则Controller,到com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2
类的源码中找到这一段:
@Autowired @Qualifier("flowRuleDefaultProvider") private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider; @Autowired @Qualifier("flowRuleDefaultPublisher") private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
修改@Qualifier
注解的内容,将其修改成:
@Autowired @Qualifier("flowRuleNacosProvider") private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider; @Autowired @Qualifier("flowRuleNacosPublisher") private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
第四步:打开sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html
文件,找到一段被注释的代码,以下:
<!--<li ui-sref-active="active" ng-if="entry.appType==0">--> <!--<a ui-sref="dashboard.flow({app: entry.app})">--> <!--<i class="glyphicon glyphicon-filter"></i> 流控规则 V1</a>--> <!--</li>-->
只须要把注释解开,即改成:
<li ui-sref-active="active" ng-if="entry.appType==0"> <a ui-sref="dashboard.flow({app: entry.app})"> <i class="glyphicon glyphicon-filter"></i> 流控规则 V1</a> </li>
到这步为止,咱们就对sentinel-dashboard源码改造完毕了,如今流控规则就能够支持推模式的持久化了,接下来就是编译、启动以及测试。
打开idea的terminal,执行以下命令进行打包编译:
mvn clean package -DskipTests
而后进入target目录,使用以下命令执行jar包,启动Sentinel控制台:
java -jar sentinel-dashboard.jar
注:也能够选择直接在idea中点击启动按钮来启动Sentinel控制台,效果是同样的
启动完成后,使用浏览器打开,能够看到Sentinel的菜单栏中比以前多出了一项流控规则 V1:
注:若没有显示该项,能够尝试清除浏览器缓存或换个浏览器打开
改造完控制台后,接下来开始改造客户端,首先在项目中添加以下依赖:
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
而后添加各个规则配置:
spring: cloud: sentinel: datasource: # 名称随意 flow: nacos: server-addr: localhost:8848 dataId: ${spring.application.name}-flow-rules groupId: SENTINEL_GROUP # 规则类型,取值见: # org.springframework.cloud.alibaba.sentinel.datasource.RuleType rule-type: flow degrade: nacos: server-addr: localhost:8848 dataId: ${spring.application.name}-degrade-rules groupId: SENTINEL_GROUP rule-type: degrade system: nacos: server-addr: localhost:8848 dataId: ${spring.application.name}-system-rules groupId: SENTINEL_GROUP rule-type: system authority: nacos: server-addr: localhost:8848 dataId: ${spring.application.name}-authority-rules groupId: SENTINEL_GROUP rule-type: authority param-flow: nacos: server-addr: localhost:8848 dataId: ${spring.application.name}-param-flow-rules groupId: SENTINEL_GROUP rule-type: param-flow
完成以上步骤后重启项目,而后回到Sentinel控制台里的流控规则 V1中新增流控规则,之因此不在簇点链路中添加,是由于簇点链路中的按钮依旧是调用以前的逻辑添加到内存中。新增流控规则以下:
新增完成后,到Nacos Server的配置列表上,能够看到该规则的配置数据,证实已经持久化存储到Nacos了:
若直接在Nacos上修改流控规则,而后刷新Sentinel控制台,控制台上的显示也会被修改
此时重启Sentinel控制台和微服务,而后刷新控制台,能够发现该流控规则依旧存在:
以上折腾了那么多只实现了流控规则的持久化,这仍是由于官方准备好了示例代码。Sentinel有若干种规则,例如降级规则、系统规则、受权规则、热点规则等,都须要使用相似的方式,修改 com.alibaba.csp.sentinel.dashboard.controller
包中对应的Controller,才能实现持久化,因此本小节仅是抛砖引玉,其余规则能够自行动手参考着实现。
推模式优缺点:
官方文档:
在生产环境使用Sentinel是必需要实现规则持久化的,而经过以上两个小节的学习,咱们能够得知无论使用哪一个模式都须要进行相应的改动,其中想要实现贴近生产使用的推模式须要改动的地方更多更麻烦。
若是不想作这些麻烦的改动,又但愿在生产环境使用Sentinel的话,则须要考虑使用阿里云提供的在线托管Sentinel控制台(AHAS),该在线Sentinel控制台实现了推模式的规则持久化并可用于生产:
接下来演示一下如何使用这个在线的Sentinel控制台,根据开通说明文档注册了阿里云帐户后,进入开通页面:
根据提示开通完成后,进入管理控制台,点击接入应用流控:
进入应用接入界面后能够选择不一样的应用接入,这里按照Spring Boot应用接入的说明完成相应步骤:
须要说明一下的是,第二步中的HTTP接口埋点和普通接口埋点均可以省略掉,由于spring-cloud-starter-alibaba-sentinel
依赖包里已经实现了,但须要排除该依赖中的sentinel-transport-simple-http
模块,避免链接了本地的Sentinel控制台,即修改依赖以下并添加AHAS Client依赖:
<!-- Sentinel --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <!-- 排除该模块的目的是不与本地的Sentinel控制台进行通讯,以避免形成没必要要的干扰 --> <exclusions> <exclusion> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-simple-http</artifactId> </exclusion> </exclusions> </dependency> <!-- ahas client --> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>spring-boot-starter-ahas-sentinel-client</artifactId> <version>1.3.3</version> </dependency>
除了修改依赖之外,还须要将配置文件中关于Sentinel控制台的配置都注释掉,由于此时咱们链接的是在线的Sentinel控制台。以下:
而后只须要添加AHAS的启动参数:
ahas: license: xxxxxxxxx namespace: default project: name: ${spring.application.name}
完成以上步骤后,重启项目并访问该项目的接口,应用正常接入的状况下能够在阿里云的控制台中看到该应用的展现,以下:
点击该应用就能够进入到Sentinel控制台,能够看到这里基本上和咱们本身搭建的Sentinel控制台是差很少的,一样支持实时监控、查看簇点链路及配置各类规则:
例如流控规则的添加也是同样的,只是界面稍微好看一点而已:
至此就完成将应用接入AHAS了,因为操做与Sentinel控制台基本同样这里就不展开介绍了,能够自行测试捣鼓一下,反正可视化的页面使用起来仍是比较容易的。