在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控制檯基本同樣這裏就不展開介紹了,能夠自行測試搗鼓一下,反正可視化的頁面使用起來仍是比較容易的。