1. 程式人生 > 實用技巧 >SpringCloud入門實戰(10)-Sentinel使用二

SpringCloud入門實戰(10)-Sentinel使用二

在Sentinel控制檯中配置的規則,預設是儲存在記憶體中的,重啟後就會丟失;所有需要把規則持久化。

1、規則管理及推送

推送模式說明優點缺點
原始模式 API 將規則推送至客戶端並直接更新到記憶體中,擴充套件寫資料來源(WritableDataSource 簡單,無任何依賴 不保證一致性;規則儲存在記憶體中,重啟即消失。嚴重不建議用於生產環境
Pull 模式 擴充套件寫資料來源(WritableDataSource), 客戶端主動向某個規則管理中心定期輪詢拉取規則,這個規則中心可以是 RDBMS、檔案 等 簡單,無任何依賴;規則持久化 不保證一致性;實時性不保證,拉取過於頻繁也可能會有效能問題。
Push 模式 擴充套件讀資料來源(ReadableDataSource),規則中心統一推送,客戶端通過註冊監聽器的方式時刻監聽變化,比如使用 Nacos、Zookeeper 等配置中心。這種方式有更好的實時性和一致性保證。生產環境下一般採用 push 模式的資料來源。 規則持久化;一致性;快速 引入第三方依賴

1.1、原始模式

如果不做任何修改,Dashboard 的推送規則方式是通過 API 將規則推送至客戶端並直接更新到記憶體中:

這種做法的好處是簡單,無依賴;壞處是應用重啟規則就會消失,僅用於簡單測試,不能用於生產環境。

1.2、Pull模式

pull 模式的資料來源(如本地檔案、RDBMS 等)一般是可寫入的。使用時需要在客戶端註冊資料來源:將對應的讀資料來源註冊至對應的 RuleManager,將寫資料來源註冊至 transport 的 WritableDataSourceRegistry 中。本地檔案資料來源會定時輪詢檔案的變更,讀取規則。這樣我們既可以在應用本地直接修改檔案來更新規則,也可以通過 Sentinel 控制檯推送規則。以本地檔案資料來源為例,推送過程如下圖所示:

使用 pull 模式的資料來源時一般不需要對 Sentinel 控制檯進行改造。這種實現方法好處是簡單,不引入新的依賴,壞處是無法保證監控資料的一致性。

1.3、Push模式

生產環境下一般更常用的是 push 模式的資料來源。對於 push 模式的資料來源,如遠端配置中心(ZooKeeper, Nacos, Apollo等等),推送的操作不應由 Sentinel 客戶端進行,而應該經控制檯統一進行管理,直接進行推送,資料來源僅負責獲取配置中心推送的配置並更新到本地。因此推送規則正確做法應該是配置中心控制檯/Sentinel 控制檯 → 配置中心 → Sentinel 資料來源 → Sentinel,而不是經 Sentinel 資料來源推送至配置中心。

2、規則持久化

2.1、Pull模式持久化

該模式只需要在應用中進行改造,不需要改造sentinel控制檯。

2.1.1、引入依賴

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-extension</artifactId>
    <version>1.8.0</version>
</dependency>

2.1.2、編寫持久化類

package com.inspur.scdemo.server.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.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.List;

/**
 * 檔案資料來源
 */
public class FileDataSourceInitFunc implements InitFunc {
    private ObjectMapper objectMapper = new ObjectMapper();
    /**流控規則物件轉換*/
    private Converter<String, List<FlowRule>> flowRuleConverter = source -> {
        try {
            return objectMapper.readValue(source, new TypeReference<List<FlowRule>>(){});
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    };
    /**降級規則物件轉換*/
    private Converter<String, List<DegradeRule>> degradeRuleConverter = source -> {
        try {
            return objectMapper.readValue(source, new TypeReference<List<DegradeRule>>(){});
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    };
    /**系統規則物件轉換*/
    private Converter<String, List<SystemRule>> systemRuleConverter = source -> {
        try {
            return objectMapper.readValue(source, new TypeReference<List<SystemRule>>(){});
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    };
    /**授權規則物件轉換*/
    private Converter<String, List<AuthorityRule>> authorityRuleConverter = source -> {
        try {
            return objectMapper.readValue(source, new TypeReference<List<AuthorityRule>>(){});
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    };
    /**授權規則物件轉換*/
    private Converter<String, List<ParamFlowRule>> paramFlowRuleConverter = source -> {
        try {
            return objectMapper.readValue(source, new TypeReference<List<ParamFlowRule>>(){});
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    };

    @Override
    public void init() throws Exception {
        String ruleDir = "d:/temp/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";

        //流控規則
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(flowRulePath, flowRuleConverter);
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());

        WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(flowRulePath, this::encodeJson);
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);

        //降級規則
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(degradeRulePath, degradeRuleConverter);
        DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());

        WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(degradeRulePath, this::encodeJson);
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);

        //系統規則
        ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(systemRulePath, systemRuleConverter);
        SystemRuleManager.register2Property(systemRuleRDS.getProperty());
        WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(systemRulePath, this::encodeJson);
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);

        //授權規則
        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(flowRulePath, authorityRuleConverter);
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(authorityRulePath, this::encodeJson);
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);

        //熱點引數規則
        ReadableDataSource<String, List<ParamFlowRule>> hotParamFlowRuleRDS = new FileRefreshableDataSource<>(paramFlowRulePath,paramFlowRuleConverter);
        ParamFlowRuleManager.register2Property(hotParamFlowRuleRDS.getProperty());
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(paramFlowRulePath,this::encodeJson);
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
    }

    private <T> String encodeJson(T t) {
        try {
            return objectMapper.writeValueAsString(t);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }
}

2.1.3、啟用持久化類

資源目錄(通常是resource目錄)建立META-INF/services目錄,並在該目錄下建立com.alibaba.csp.sentinel.init.InitFunc檔案,內容為:

com.inspur.scdemo.server.sentinel.FileDataSourceInitFunc

2.1.3、驗證

啟動sentinel控制檯並啟動應用,配置規則後,可以看到生成了對應的檔案,檔案內容就是規則資訊:

2.2、Push模式持久化

該模式需同時改造sentinel控制檯和自己的應用。

2.2.1、控制檯改造

2.2.1.1、下載sentinel原始碼

https://github.com/alibaba/Sentinel

2.2.1.2、修改原sentinel-dashboard專案下的POM檔案
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <!--scope>test</scope-->
</dependency>        

註釋掉scope

2.2.1.3、複製sentinel-dashboard專案下test中的nacos程式碼

src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos到src/main/java/com/alibaba/csp/sentinel/dashboard/rule下

2.2.1.4、修改NacosConfig中的nacos服務地址
com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfig:
@Bean
public ConfigService nacosConfigService() throws Exception {
    return ConfigFactory.createConfigService("localhost:8848");
}
2.2.1.5、修改FlowControllerV2中的ruleProvider和rulePublisher

com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2:

@Autowired
@Qualifier("flowRuleDefaultProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleDefaultPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;

改為:

@Autowired
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
2.2.1.6、sidebar.html流控規則路由從dashboard.flowV1改成dashboard.flow

sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html:

<li ui-sref-active="active" ng-if="!entry.isGateway">
  <a ui-sref="dashboard.flow({app: entry.app})">
    <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控規則</a>
</li>

注意:在該頁面新增規則與在簇點鏈路頁面新增流控規則不是同一頁面,簇點鏈路頁面新增規則需另行改造。

2.2.1.7、其他規則改造

上面的步驟只是改造了流控規則,其他的規則可用參考流控規則進行改造。

2.2.2、應用改造

2.2.2.1、引入依賴
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <version>1.8.0</version>
</dependency>            
2.2.2.2、增加規則資料來源配置
spring:
  cloud:
    sentinel:
      transport:
        port: 8719
        dashboard: 10.49.196.10:8080
      datasource:
        flow:
          nacos:
            server-addr: 10.49.196.10:8848
            dataId: ${spring.application.name}-flow-rules
            groupId: SENTINEL_GROUP
            rule-type: flow
        degrade:
          nacos:
            server-addr: 10.49.196.10:8848
            dataId: ${spring.application.name}-degrade-rules
            groupId: SENTINEL_GROUP
            rule-type: degrade

這邊只配置了流控及降級的資料來源,其他規則類似配置。

2.2.3、驗證

啟動nacos、sentinel控制檯及應用,在sentinel配置規則後,在nacos控制檯中可以看到對應的配置,配置內容就是規則資訊: