Spring Cloud Alibaba基礎教程:Sentinel Dashboard同步Apollo存儲規則

在之前的兩篇教程中我們分別介紹了如何將Sentinel的限流規則存儲到Nacos和Apollo中。同時,在文末的思考中,我都指出了這兩套整合方案都存在一個不足之處:不論采用什么配置中心,限流規則都只能通過Nacos界面或Apollo界面來完成修改才能得到持久化存儲,而在Sentinel Dashboard中修改限流規則雖然可以生效,但是不會被持久化到配置中心。而在這兩個配置中心里存儲的數據是一個Json格式,當存儲的規則越來越多,對該Json配置的可讀性與可維護性會變的越來越差。所以,下面我們就來繼續探討這個不足之處,并給出相應的解決方案。本文以Apollo存儲為例,下一篇介紹Nacos的改在示例。

問題分析

在實際操作之前,我們先通過下圖了解一下之前我們所實現的限流規則持久化方案的配置數據流向圖:

image.png
  • 藍色箭頭代表了限流規則由配置中心發起修改的更新路徑
  • 橙色箭頭代表了限流規則由Sentinel Dashboard發起修改的更新路徑

從圖中可以很明顯的看到,Sentinel Dashboard與業務服務之間本身是可以互通獲取最新限流規則的,這在沒有整合配置中心來存儲限流規則的時候就已經存在這樣的機制。最主要的區別是:配置中心的修改都可以實時的刷新到業務服務,從而被Sentinel Dashboard讀取到,但是對于這些規則的更新到達各個業務服務之后,并沒有一個機制去同步到配置中心,作為配置中心的客戶端也不會提供這樣的逆向更新方法。

改造方案

關于如何改造,現來解讀一下官方文檔中關于這部分的說明:

要通過 Sentinel 控制臺配置集群流控規則,需要對控制臺進行改造。我們提供了相應的接口進行適配。

從 Sentinel 1.4.0 開始,我們抽取出了接口用于向遠程配置中心推送規則以及拉取規則:

  • DynamicRuleProvider<T>: 拉取規則
  • DynamicRulePublisher<T>: 推送規則

對于集群限流的場景,由于每個集群限流規則都需要唯一的 flowId,因此我們建議所有的規則配置都通過動態規則源進行管理,并在統一的地方生成集群限流規則。

我們提供了新版的流控規則頁面,可以針對應用維度推送規則,對于集群限流規則可以自動生成 flowId。用戶只需實現 DynamicRuleProvider 和 DynamicRulePublisher 接口,即可實現應用維度推送(URL: /v2/flow)。

這段內容什么意思呢?簡單的說就是Sentinel Dashboard通過DynamicRuleProviderDynamicRulePublisher兩個接口來獲取和更新應用的動態規則。默認情況下,就如上一節中Sentinel Dashboard與各業務服務之間的兩個箭頭,一個接口負責獲取規則,一個接口負責更新規則。

所以,只需要通過這兩個接口,實現對配置中心中存儲規則的讀寫,就能實現Sentinel Dashboard中修改規則與配置中心存儲同步的效果。

具體的配置數據流向圖如下:

image.png

其中,綠色箭頭為公共公共部分,即:不論從培中心修改,還是從Sentinel Dashboard修改都會觸發的操作。這樣的話,從上圖的兩處修改起點看,所有涉及的部分都能獲取到一致的限流規則了。

代碼實現

下面繼續說說具體的代碼實現,這里參考了Sentinel Dashboard源碼中關于Apollo實現的測試用例。但是由于考慮到與Spring Cloud Alibaba的結合使用,略作修改。

第一步:修改pom.xml中的Apollo OpenAPi的依賴,將<scope>test</scope>注釋掉,這樣才能在主程序中使用。

<dependency>
    <groupId>com.ctrip.framework.apollo</groupId>
    <artifactId>apollo-openapi</artifactId>
    <version>1.2.0</version>
    <!--<scope>test</scope>-->
</dependency>

第二步:找到resources/app/scripts/directives/sidebar/sidebar.html中的這段代碼:

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

修改為:

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

第三步:在com.alibaba.csp.sentinel.dashboard.rule包下新建一個apollo包,用來編寫針對Apollo的擴展實現。

第四步:創建Apollo的配置類,定義Apollo的portal訪問地址以及第三方應用訪問的授權Token(通過Apollo管理員賬戶登錄,在“開放平臺授權管理”功能中創建),具體代碼如下:

@Configuration
public class ApolloConfig {

    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }

    @Bean
    public ApolloOpenApiClient apolloOpenApiClient() {
        ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder()
            .withPortalUrl("https://apollo.xxx.com")  // TODO 根據實際情況修改
            .withToken("open api token") // TODO 根據實際情況修改
            .build();
        return client;
    }

}

第五步:實現Apollo的配置拉取實現。

@Component("flowRuleApolloProvider")
public class FlowRuleApolloProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {

    @Autowired
    private ApolloOpenApiClient apolloOpenApiClient;
    @Autowired
    private Converter<String, List<FlowRuleEntity>> converter;

    @Value("${env:DEV}")
    private String env;

    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception {
        // flowDataId對應
        String flowDataId = "sentinel.flowRules";
        OpenNamespaceDTO openNamespaceDTO = apolloOpenApiClient.getNamespace(appName, env, "default", "application");
        String rules = openNamespaceDTO
            .getItems()
            .stream()
            .filter(p -> p.getKey().equals(flowDataId))
            .map(OpenItemDTO::getValue)
            .findFirst()
            .orElse("");

        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}
  • getRules方法中的appName參數是Sentinel中的服務名稱,這里直接通過這個名字獲取Apollo配置是由于Apollo中的項目AppId與之一致,如果存在不一致的情況,則需要自己做轉換。
  • 這里注入了一個env屬性,主要由于我們在使用Apollo的時候,通過啟動參數來控制不同環境。所以這樣就能在不同環境區分不同的限流配置了。
  • 這里的flowDataId對應各個微服務應用中定義的spring.cloud.sentinel.datasource.ds.apollo.flowRulesKey配置,即:Apollo中使用了什么key來存儲限流配置。
  • 其他如Cluster、Namepsace都采用了默認值:default和application,這個讀者有特殊需求可以做對應的修改。

第六步:實現Apollo的配置推送實現。

@Component("flowRuleApolloPublisher")
public class FlowRuleApolloPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {

    @Autowired
    private ApolloOpenApiClient apolloOpenApiClient;
    @Autowired
    private Converter<List<FlowRuleEntity>, String> converter;

    @Value("${env:DEV}")
    private String env;

    @Override
    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
        String flowDataId = "sentinel.flowRules";

        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }

        OpenItemDTO openItemDTO = new OpenItemDTO();
        openItemDTO.setKey(flowDataId);
        openItemDTO.setValue(converter.convert(rules));
        openItemDTO.setComment("modify by sentinel-dashboard");
        openItemDTO.setDataChangeCreatedBy("apollo");
        apolloOpenApiClient.createOrUpdateItem(app, env, "default", "application", openItemDTO);

        // Release configuration
        NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO();
        namespaceReleaseDTO.setEmergencyPublish(true);
        namespaceReleaseDTO.setReleaseComment("release by sentinel-dashboard");
        namespaceReleaseDTO.setReleasedBy("apollo");
        namespaceReleaseDTO.setReleaseTitle("release by sentinel-dashboard");
        apolloOpenApiClient.publishNamespace(app, env, "default", "application", namespaceReleaseDTO);
    }
}
  • 這里的大部分內容,如:env、flowDataId、app說明與上一步中的實現一致
  • openItemDTO.setDataChangeCreatedBy("apollo");namespaceReleaseDTO.setReleasedBy("apollo");這兩句需要注意一下,必須設置存在并且有權限的用戶,不然會更新失敗。

第七步:修改com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2DynamicRuleProviderDynamicRulePublisher注入的Bean,改為上面我們編寫的針對Apollo的實現:

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

代碼示例

本文介紹內容的客戶端代碼,示例讀者可以通過查看下面倉庫中的alibaba-sentinel-dashboard-apollo項目:

如果您對這些感興趣,歡迎star、follow、收藏、轉發給予支持!

系列回顧

專題推薦

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,401評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,635評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,128評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,365評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,881評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,733評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,935評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,172評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,595評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,908評論 2 372

推薦閱讀更多精彩內容