Sentinel Client: 整合Apollo規則持久化


在前面的學習過程中,Sentinel 的規則,也就是我們之前定義的限流規則,是通過代碼的方式定義好的。這是初始化時需要做的事情,Sentinel 提供了基於API的方式修改規則:

FlowRuleManager.loadRules(List<FlowRule> rules); // 修改流控規則
DegradeRuleManager.loadRules(List<DegradeRule> rules); // 修改降級規則
SystemRuleManager.loadRules(List<SystemRule> rules); // 修改系統規則
AuthorityRuleManager.loadRules(List<AuthorityRule> rules); // 修改授權規則

當我們接入了控制台后,可以通過控制台進行規則的動態修改,問題是當應用程序重啟后規則信息就會恢復到初始化的階段,也就是說后面修改的值會丟失,因為規則信息都是存儲在應用的內存中。

為了解決這個問題Sentinel 提供了DataSource 擴展的功能,官方推薦通過控制台設置規則后將規則推送到統一的規則中心,客戶端實現 ReadableDataSource 接口端監聽規則中心實時獲取變更,流程如下:

擴展的常見方式有推和拉兩種模式:

  • 拉模式:客戶端主動向某個規則管理中心定期輪詢拉取規則,這個規則中心可以是 RDBMS、文件,甚至是 VCS 等。這樣做的方式是簡單,缺點是無法及時獲取變更;
  • 推模式:規則中心統一推送,客戶端通過注冊監聽器的方式時刻監聽變化,比如使用 NacosApollo、Zookeeper 等配置中心。這種方式有更好的實時性和一致性保證。

今天我們主要是講如何使用 Apollo 來配置規則進行持久化,Apollo是攜程開源的配置中心,非常好用

Github地址:https://github.com/ctripcorp/apollo

在我的書中也有對Apollo使用的詳細介紹,等出版了再通知大家。

首先集成需要的依賴:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-apollo</artifactId>
    <version>1.4.1</version>
</dependency>

然后創建 ApolloDataSource 並將其注冊至對應的 RuleManager 上即可。比如:

private static void loadRules() {
		// Apollo 中的應用名稱,自己定義的
        String appId = "SampleApp";
        // Apollo 的地址
        String apolloMetaServerAddress = "http://localhost:8080";
        System.setProperty("app.id", appId);
        System.setProperty("apollo.meta", apolloMetaServerAddress);
        // 指定環境
        System.setProperty("env", "DEV");
        // Apollo 的命名空間
        String namespaceName = "application";
        // 限流規則的Key, 在Apollo中用此Key
        String flowRuleKey = "flowRules";
        // 限流規則的默認值
        String defaultFlowRules = "[]";
        // 注冊數據源
        ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new ApolloDataSource<>(namespaceName,
            flowRuleKey, defaultFlowRules, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
        }));
        FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
}

到此為止配置就結束了,詳細的解釋我都寫了注釋哈。官方文檔也是這么寫的,問題是如果你剛接觸會一頭霧水的,為什么?

你不知道在Apollo中怎么配置啊,我們講的就是說可以用Apollo來作為存儲,持久化規則,那么規則怎么配置就需要我們自己去想。

我也是通過看源碼才知道怎么去配置的,帶着大家一起來看源碼吧!

主要就是new ApolloDataSource這里,參數都是通過這里傳進去的

public ApolloDataSource(String namespaceName, String flowRulesKey, String defaultFlowRuleValue,
                            Converter<String, T> parser) {
        super(parser);

        Preconditions.checkArgument(!Strings.isNullOrEmpty(namespaceName), "Namespace name could not be null or empty");
        Preconditions.checkArgument(!Strings.isNullOrEmpty(flowRulesKey), "FlowRuleKey could not be null or empty!");

        this.flowRulesKey = flowRulesKey;
        this.defaultFlowRuleValue = defaultFlowRuleValue;

        this.config = ConfigService.getConfig(namespaceName);

        initialize();

        RecordLog.info(String.format("Initialized rule for namespace: %s, flow rules key: %s",
            namespaceName, flowRulesKey));
    }

這邊就是對傳入的參數賦值,然后看下面這行:

this.config = ConfigService.getConfig(namespaceName);

這就是通過命名空間去Apollo中獲取配置,獲取完后就執行初始化

private void initialize() {
     initializeConfigChangeListener();
     loadAndUpdateRules();
}

initializeConfigChangeListener是初始化配置的監聽器,當配置發生修改時會進入該監聽器,也就是說在這個監聽器里需要監聽配置的修改,然后更新規則

 private void initializeConfigChangeListener() {
        config.addChangeListener(new ConfigChangeListener() {
            @Override
            public void onChange(ConfigChangeEvent changeEvent) {
                ConfigChange change = changeEvent.getChange(flowRulesKey);
                //change is never null because the listener will only notify for this key
                if (change != null) {
                    RecordLog.info("[ApolloDataSource] Received config changes: " + change.toString());
                }
                loadAndUpdateRules();
            }
        }, Sets.newHashSet(flowRulesKey));
 }

loadAndUpdateRules就是更新規則的邏輯了

private void loadAndUpdateRules() {
        try {
            T newValue = loadConfig();
            if (newValue == null) {
                RecordLog.warn("[ApolloDataSource] WARN: rule config is null, you may have to check your data source");
            }
            getProperty().updateValue(newValue);
        } catch (Throwable ex) {
            RecordLog.warn("[ApolloDataSource] Error when loading rule config", ex);
        }
 }

那么配置是怎么來的呢,請看loadConfig

 @Override
 public T loadConfig() throws Exception {
     return loadConfig(readSource());
 }

 public T loadConfig(S conf) throws Exception {
     T value = parser.convert(conf);
     return value;
 }

readSource就是獲取我們配置的flowRulesKey的值,那么配置其實就是一個字符串,然后下面通過Json轉換

 public String readSource() throws Exception {
     return config.getProperty(flowRulesKey, defaultFlowRuleValue);
 }

我們再返過來看看注冊的代碼:

 // 注冊數據源
 ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new ApolloDataSource<>(namespaceName,
            flowRuleKey, defaultFlowRules, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
 }));

重點是ource -> JSON.parseObject(source, new TypeReference<List >()這行,這不就是轉換成List 嗎,真相呼之欲出了,也就是在Apollo中配置的就是List 的json格式就行。

我們配置一個試試看:

flowRules = [{"grade":1,"count":11,"resource":"HelloWorld"}]

點擊保存並且發布,可以在initializeConfigChangeListener里面設置一個斷點,你會發現,當發布配置之后,這邊馬上就會進來,然后執行其他的邏輯,到此為止整個流程結束。

歡迎加入我的知識星球,一起交流技術,免費學習猿天地的課程(http://cxytiandi.com/course)

PS:目前星球中正在星主的帶領下組隊學習Sentinel,等你哦!

微信掃碼加入猿天地知識星球

猿天地


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM