【轉(zhuǎn)】【Sentinel】sentinel 集成 apollo 最佳實(shí)踐

原文:https://www.cnblogs.com/kiwifly/p/11569190.html

【Sentinel】sentinel 集成 apollo 最佳實(shí)踐

sentinel

前言

在 sentinel 的控制臺(tái)設(shè)置的規(guī)則信息默認(rèn)都是存在內(nèi)存當(dāng)中的。所以無論你是重啟了 sentinel 的客戶端還是 sentinel 的控制臺(tái)。你所設(shè)置的規(guī)則都會(huì)丟失。如果想要 sentinel 在線上環(huán)境使用,要么花錢用阿里云上的付費(fèi)版本,要么自己去實(shí)現(xiàn)規(guī)則的持久化,如果你或你所在的公司不差錢,那么關(guān)掉這篇文章,直接用付費(fèi)版吧,省掉了一大堆坑要踩?;蛘吣闶且粋€(gè)特立獨(dú)行的人,那么我們接著往下說。
??首先說一下寫這篇文章的原因,因?yàn)檎娴迷谂c apllo 集成時(shí),踩坑踩到懷疑人生。另一點(diǎn)是,找了一大堆關(guān)于集成的 apollo 的文章,都清一色的都是仿照官方給的限流規(guī)則的 DEMO 做的。 但是 sentinel 規(guī)則還有熔斷規(guī)則、參數(shù)限流、系統(tǒng)限流、黑白名單等很多規(guī)則,每個(gè)規(guī)則還有細(xì)節(jié)上的不一致,這些都沒有提,還有一些客戶端的坑就更沒有了。踩了這么多坑,有了一點(diǎn)心得與體會(huì),梳理與此,希望能幫助到讀者。

拉取 sentinel 控制臺(tái)源碼進(jìn)行修改

因?yàn)樾薷膬?nèi)容過多,本文不會(huì)詳述,下面的截圖是所有修改內(nèi)容,并且因?yàn)閷戇@篇文章時(shí),1.7版本在 master 開發(fā),有大量快照版本。所以是切到當(dāng)前穩(wěn)定的 1.6 分支進(jìn)行修改的。我已經(jīng) fork sentinel 到我的 github,下面是修改的內(nèi)容 地址

修改點(diǎn)1:實(shí)現(xiàn)所有規(guī)則的拉取與推送接口

添加與實(shí)現(xiàn)了所有的規(guī)則的 Provider 與 Publisher 的配置拉取的與推送。

修改點(diǎn)2:修改控制臺(tái)使用的規(guī)則操作 api

規(guī)則在控制臺(tái)的操作 controller 進(jìn)行大量改造。

修改點(diǎn)3:修改 xxEntity

最后一點(diǎn)也是最坑的修改了大量的 xxEntity 類,這些類是規(guī)則的實(shí)體類,本身沒什么,源碼是直接 json 化保存的,但是用于客戶端集成的 spring-cloud-alibaba 使用了 json 校驗(yàn),如果 apollo 保存的 json 與客戶端的實(shí)體類有一丁點(diǎn)不一樣就報(bào) convert 0 rules but there are 1 rules from datasource . RuleCLass: FlowRule 。是不是覺得很摸不著頭腦,這報(bào)錯(cuò)跟 json 格式轉(zhuǎn)換錯(cuò)誤有什么,下面是 spring-cloud-alibaba json 轉(zhuǎn)換的代碼。

寫這段代碼的老哥,把這個(gè)異常吃了,并補(bǔ)上了一個(gè) // won't be happen 的注釋,你能理解我當(dāng)時(shí)被這個(gè)報(bào)錯(cuò)坑的死去活來,后來發(fā)現(xiàn)是這里的問題嗎?后來在 github 上找到兩個(gè)同樣的問題問題1問題2,按照方法把 xxEntity 中用不到的字段全部加上 @JSONField(serialize = false) 解決。

修改點(diǎn)4:抽離配置使得可以在啟動(dòng)的時(shí)傳入

添加的配置在下面

使用修改的控制臺(tái)版本

  1. 你可以fork sentinel 官方代碼按上述的自行修改,然后打包
  2. 你可以拉取我 fork 的 sentinel 代碼下來直接打包
  3. 你可以使用我已經(jīng)打好的包 地址

自定義配置

配置名稱 是否必填 默認(rèn)值 作用
env DEV 指定 apollo 使用的環(huán)境
app.id sentinel-apollo 指定保存 sentinel 規(guī)則的 apollo 應(yīng)用 ID
cluster.name default 指定保存 sentinel 使用的 apollo 集群
namespace.name application 指定保存 sentinel 使用的 apollo 命名空間
modify.user apollo 控制臺(tái)顯示的修改人賬號,此賬號務(wù)必要有此應(yīng)用的權(quán)限
modify.comment modify by sentinel-dashboard apollo 控制臺(tái)顯示的修改備注
release.user apollo 控制臺(tái)顯示的發(fā)布人賬號,此賬號務(wù)必要有此應(yīng)用的權(quán)限
release.comment release by sentinel-dashboard apollo 控制臺(tái)顯示的發(fā)布備注
apollo.portal.url apollo 控制臺(tái)的地址
apollo.application.token 指定保存 sentinel 規(guī)則的 apollo 應(yīng)用 openapi 的 token
authority.key.suffix authority 認(rèn)證規(guī)則保存在 apollo 中的 key 的后綴
degrade.key.suffix degrade 熔斷規(guī)則保存在 apollo 中的 key 的后綴
flow.key.suffix flow 限流規(guī)則保存在 apollo 中的 key 的后綴
param.key.suffix param_flow 參數(shù)限流規(guī)則保存在 apollo 中的 key 的后綴
system.key.suffix system 系統(tǒng)限流規(guī)則保存在 apollo 中的 key 的后綴
auth.username sentinel sentinel 控制臺(tái)的登錄用戶名
auth.password sentinel sentinel 控制臺(tái)的登錄密碼
server.port 8080 sentinel 控制臺(tái)的啟動(dòng)端口

Apollo 配置

創(chuàng)建用于保存 sentinel 的項(xiàng)目

  1. 點(diǎn)擊創(chuàng)建項(xiàng)目按鈕


  2. 輸入項(xiàng)目信息


    1. 應(yīng)用 ID 對應(yīng) 上面表格中的 app.id
    2. 應(yīng)用負(fù)責(zé)人 對應(yīng) 上面表格中的 modify.userrelease.user
  3. 創(chuàng)建一個(gè)公共命名空間

    1. 點(diǎn)擊右下角添加 Namespace 按鈕


    2. 創(chuàng)建 Namespace


      1. 名稱對應(yīng)上面表格中的 namespace.name,注意名稱是要包含部門名的,這里脫敏了
      2. 類型一定要選擇 public ,原因后面會(huì)說
  4. 發(fā)布 Namespace


    1. 私有的空間是不能被繼承的,application 沒有用,可以刪除
      1. 這里的用意是我們獨(dú)立出一個(gè)單獨(dú)的用于保存規(guī)則的 apollo 應(yīng)用,因?yàn)槭枪驳?,所以其它apollo 應(yīng)用可以繼承,這樣對于已經(jīng)集成 apollo 的項(xiàng)目來說,改動(dòng)最小
      2. 注意紅色的提示,我們建的公共空間要首先發(fā)布一次,否則 api 無法訪問到
  5. 創(chuàng)建此項(xiàng)目的開放平臺(tái)授權(quán)

    1. 點(diǎn)擊右上角的開放平臺(tái)授權(quán)管理

    2. 創(chuàng)建應(yīng)用


      1. 第三方應(yīng)用 ID 就是你上面創(chuàng)建的項(xiàng)目的 appId
      2. 第三方應(yīng)用名稱 隨便寫
    3. 賦權(quán)


      1. token 你點(diǎn)擊創(chuàng)建應(yīng)用后自動(dòng)生成的
      2. 被管理的 Appid 就是你上面創(chuàng)建的項(xiàng)目的 appId
      3. 授權(quán)類型 一定要選 App

spring boot 集成 sentinel

源碼地址

引入依賴

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>0.9.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-apollo</artifactId>
            <version>1.5.2</version>
        </dependency>

測試 controller

package cn.coder4j.study.example.sentinel;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author buhao
 * @version TestController.java, v 0.1 2019-09-19 20:53 buhao
 */
@RequestMapping("/test")
@Controller
public class TestController {

    /**
     * 沒有注解通過自適應(yīng)的限流
     * @return
     */
    @GetMapping("/flowRule")
    @ResponseBody
    public String flowRule(){
        return "success";
    }

    /**
     * 通過手動(dòng)注解的限流
     * @return
     */
    @GetMapping("/flowRuleWithAnno")
    @ResponseBody
    @SentinelResource("flowRuleWithAnno")
    public String flowRuleWithAnno(){
        return "success";
    }

    /**
     * 參數(shù)限流規(guī)則測試
     * @param param
     * @return
     */
    @GetMapping("/paramFlowRule")
    @ResponseBody
    @SentinelResource("paramFlowRule")
    public String paramFlowRule(String param){
        return "success";
    }

    /**
     * 熔斷規(guī)則測試
     * @return
     */
    @GetMapping("/degradeRule")
    @ResponseBody
    @SentinelResource("degradeRule")
    public String degradeRule(){
        throw new RuntimeException("服務(wù)器異常");
    }
}

配置 application.yml

apollo:
  bootstrap:
    enabled: true # 開啟 apollo
  meta: xxx       # 指定 apollo 注冊地址
app:
  id: sentinel-apollo  # 指定規(guī)則項(xiàng)目在 apollo 的app.id,要與 sentinel 控制臺(tái)啟動(dòng)參數(shù)一致
spring:
  application:
    name: study-sentinel-example  # 應(yīng)用名稱,不同項(xiàng)目要唯一,會(huì)把他做為規(guī)則 Key 的前綴
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8989  # sentinel 控制臺(tái)的地址
      datasource:
        ds1:
          apollo:
            namespace-name: {部門名}.sentinel-rule  # 保存規(guī)則的 apollo 應(yīng)用的公共 namespace, 要與 sentinel 控制臺(tái)啟動(dòng)參數(shù)一致
            rule-type: flow   # 指定該數(shù)據(jù)源是限流規(guī)則
            flow-rules-key: ${spring.application.name}-${spring.cloud.sentinel.datasource.ds1.apollo.rule-type} # 指定該規(guī)則在 apollo 應(yīng)用中 key 名稱
        ds2:
          apollo:
            namespace-name: {部門名}.sentinel-rule
            rule-type: degrade # 指定該數(shù)據(jù)源是熔斷規(guī)則
            flow-rules-key: ${spring.application.name}-${spring.cloud.sentinel.datasource.ds2.apollo.rule-type} # 指定該規(guī)則在 apollo 應(yīng)用中 key 名稱
        ds3:
          apollo:
            namespace-name: {部門名}.sentinel-rule
            rule-type: param_flow # 指定該數(shù)據(jù)源是參數(shù)限流規(guī)則
            flow-rules-key: ${spring.application.name}-${spring.cloud.sentinel.datasource.ds3.apollo.rule-type} # 指定該規(guī)則在 apollo 應(yīng)用中 key 名稱
        ds4:
          apollo:
            namespace-name: {部門名}.sentinel-rule
            rule-type: system  # 指定該數(shù)據(jù)源是系統(tǒng)限流規(guī)則
            flow-rules-key: ${spring.application.name}-${spring.cloud.sentinel.datasource.ds4.apollo.rule-type} # 指定該規(guī)則在 apollo 應(yīng)用中 key 名稱
        ds5:
          apollo:
            namespace-name: {部門名}.sentinel-rule
            rule-type: authority # 指定該數(shù)據(jù)源是認(rèn)證限流(黑白名單)規(guī)則
            flow-rules-key: ${spring.application.name}-${spring.cloud.sentinel.datasource.ds5.apollo.rule-type} # 指定該規(guī)則在 apollo 應(yīng)用中 key 名稱

sentinel 控制臺(tái)配置

  1. 第一次啟動(dòng)時(shí) sentinel 是沒有應(yīng)用數(shù)據(jù),只要請求幾次你應(yīng)用的接口就可以了


  2. 請求之后可以看到我們的應(yīng)用在右側(cè)列表了


  3. 首先點(diǎn)擊簇點(diǎn)監(jiān)控,如果是空白的話說明,接口還沒有被請求過,通過上面提供的 jmeter 腳本,可以快速訪問所有接口,訪問后如下圖所示


    1. 可以看到除了我們手動(dòng)通過注解定義的資源名,還多了一些是通過我們的 controller 路徑的資源名,這些都是我們客戶端集成 spring-cloud-starter-alibaba-sentinel 包后,自動(dòng)適配的。這兩種其實(shí)在使用上有區(qū)別的
      1. 自動(dòng)適配的限流后會(huì)返回 Blocked by Sentinel (flow limiting)
      2. 通過注解的是會(huì)拋出 UndeclaredThrowableException 異常,我們可以通過 文章 說的方法轉(zhuǎn)成我們想的限流異常處理。
    2. 右邊的操作就是添加各種規(guī)則,這里修改后會(huì)實(shí)時(shí)同步到客戶端并同步保存到 apollo

jmeter 配置

jmeter 是用于測試與驗(yàn)證規(guī)則使用的,因?yàn)榭梢栽O(shè)置線程數(shù),所以可以很好的測試限流情況。測試腳本下載

  1. 線程組要把線程設(shè)置成 100,方便后面的統(tǒng)計(jì),另外為了在一秒內(nèi)執(zhí)行完,Ramp-Up 時(shí)間設(shè)為0


  2. 請求默認(rèn)值就是填寫你本地的啟的測試項(xiàng)目的地址


  3. xx 規(guī)則填寫測試接口地址,參數(shù)限流因?yàn)橐鰧φ諏?shí)驗(yàn)所以寫了兩個(gè)


  4. 查看結(jié)果樹可以看到你每次請求的內(nèi)容與結(jié)果


  5. 可以看到上張圖片內(nèi)有紅色的有綠色的,紅色說明斷言失敗,綠色說明斷言成功,斷言的內(nèi)容就是包含 success


  6. 聚合統(tǒng)計(jì),這個(gè)可以統(tǒng)計(jì)出100個(gè)線程請求后的總體結(jié)果,我們只要看 Error% 的失敗率就可以了。圖中可以看到除了熔斷限流,其它限流失敗率是 0


測試步驟

  1. 在簇點(diǎn)鏈路中找到 /test/flowRule,并點(diǎn)擊流控按鈕

  2. 單機(jī)閾值填入 10,點(diǎn)擊新增


  3. 新增后會(huì)跳轉(zhuǎn)到流程規(guī)則頁面


  4. 運(yùn)行jemter,可以看到失敗率為90%,這代表你的限流成功了


    1. 上面說了失敗是因?yàn)閿嘌詻]有成功,也就是說沒有返回 success,那他現(xiàn)在返回的是什么呢?

    2. 察看結(jié)果樹,隨便找一條紅包的記錄,看響應(yīng)數(shù)量


    3. 可以看到返回的是 "Blocked by Sentinel (flow limiting)",這個(gè)就是集成后配置的限流頁面的返回值,可以通過 spring.cloud.sentinel.servlet.block-page 指定成你自己的頁面

  5. 另外打開 apollo 可以看到,多了一個(gè)規(guī)則,這就是你剛剛添加的限流規(guī)則


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容