天天看點

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 接口端監聽規則中心實時擷取變更,流程如下:

Sentinel Client: 整合Apollo規則持久化

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

  • 拉模式:用戶端主動向某個規則管理中心定期輪詢拉取規則,這個規則中心可以是 RDBMS、檔案,甚至是 VCS 等。這樣做的方式是簡單,缺點是無法及時擷取變更;
  • 推模式:規則中心統一推送,用戶端通過注冊監聽器的方式時刻監聽變化,比如使用 Nacos、Apollo、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裡面設定一個斷點,你會發現,當釋出配置之後,這邊馬上就會進來,然後執行其他的邏輯,到此為止整個流程結束。