原文:https://www.cnblogs.com/kiwifly/p/11569190.html
【Sentinel】sentinel 集成 apollo 最佳實(shí)踐

前言
在 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)版本
- 你可以fork sentinel 官方代碼按上述的自行修改,然后打包
- 你可以拉取我 fork 的 sentinel 代碼下來直接打包
- 你可以使用我已經(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)目
-
點(diǎn)擊創(chuàng)建項(xiàng)目按鈕
-
輸入項(xiàng)目信息
- 應(yīng)用 ID 對應(yīng) 上面表格中的 app.id
- 應(yīng)用負(fù)責(zé)人 對應(yīng) 上面表格中的 modify.user 和 release.user
-
創(chuàng)建一個(gè)公共命名空間
-
點(diǎn)擊右下角添加 Namespace 按鈕
-
創(chuàng)建 Namespace
- 名稱對應(yīng)上面表格中的 namespace.name,注意名稱是要包含部門名的,這里脫敏了
- 類型一定要選擇 public ,原因后面會(huì)說
-
-
發(fā)布 Namespace
- 私有的空間是不能被繼承的,application 沒有用,可以刪除
- 這里的用意是我們獨(dú)立出一個(gè)單獨(dú)的用于保存規(guī)則的 apollo 應(yīng)用,因?yàn)槭枪驳?,所以其它apollo 應(yīng)用可以繼承,這樣對于已經(jīng)集成 apollo 的項(xiàng)目來說,改動(dòng)最小
- 注意紅色的提示,我們建的公共空間要首先發(fā)布一次,否則 api 無法訪問到
- 私有的空間是不能被繼承的,application 沒有用,可以刪除
-
創(chuàng)建此項(xiàng)目的開放平臺(tái)授權(quán)
-
點(diǎn)擊右上角的開放平臺(tái)授權(quán)管理
-
創(chuàng)建應(yīng)用
- 第三方應(yīng)用 ID 就是你上面創(chuàng)建的項(xiàng)目的 appId
- 第三方應(yīng)用名稱 隨便寫
-
賦權(quán)
- token 你點(diǎn)擊創(chuàng)建應(yīng)用后自動(dòng)生成的
- 被管理的 Appid 就是你上面創(chuàng)建的項(xiàng)目的 appId
- 授權(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)配置
-
第一次啟動(dòng)時(shí) sentinel 是沒有應(yīng)用數(shù)據(jù),只要請求幾次你應(yīng)用的接口就可以了
-
請求之后可以看到我們的應(yīng)用在右側(cè)列表了
-
首先點(diǎn)擊簇點(diǎn)監(jiān)控,如果是空白的話說明,接口還沒有被請求過,通過上面提供的 jmeter 腳本,可以快速訪問所有接口,訪問后如下圖所示
- 可以看到除了我們手動(dòng)通過注解定義的資源名,還多了一些是通過我們的 controller 路徑的資源名,這些都是我們客戶端集成 spring-cloud-starter-alibaba-sentinel 包后,自動(dòng)適配的。這兩種其實(shí)在使用上有區(qū)別的
- 自動(dòng)適配的限流后會(huì)返回 Blocked by Sentinel (flow limiting)
- 通過注解的是會(huì)拋出 UndeclaredThrowableException 異常,我們可以通過 文章 說的方法轉(zhuǎn)成我們想的限流異常處理。
- 右邊的操作就是添加各種規(guī)則,這里修改后會(huì)實(shí)時(shí)同步到客戶端并同步保存到 apollo
- 可以看到除了我們手動(dòng)通過注解定義的資源名,還多了一些是通過我們的 controller 路徑的資源名,這些都是我們客戶端集成 spring-cloud-starter-alibaba-sentinel 包后,自動(dòng)適配的。這兩種其實(shí)在使用上有區(qū)別的
jmeter 配置
jmeter 是用于測試與驗(yàn)證規(guī)則使用的,因?yàn)榭梢栽O(shè)置線程數(shù),所以可以很好的測試限流情況。測試腳本下載
-
線程組要把線程設(shè)置成 100,方便后面的統(tǒng)計(jì),另外為了在一秒內(nèi)執(zhí)行完,Ramp-Up 時(shí)間設(shè)為0
-
請求默認(rèn)值就是填寫你本地的啟的測試項(xiàng)目的地址
-
xx 規(guī)則填寫測試接口地址,參數(shù)限流因?yàn)橐鰧φ諏?shí)驗(yàn)所以寫了兩個(gè)
-
查看結(jié)果樹可以看到你每次請求的內(nèi)容與結(jié)果
-
可以看到上張圖片內(nèi)有紅色的有綠色的,紅色說明斷言失敗,綠色說明斷言成功,斷言的內(nèi)容就是包含 success
-
聚合統(tǒng)計(jì),這個(gè)可以統(tǒng)計(jì)出100個(gè)線程請求后的總體結(jié)果,我們只要看 Error% 的失敗率就可以了。圖中可以看到除了熔斷限流,其它限流失敗率是 0
測試步驟
-
在簇點(diǎn)鏈路中找到 /test/flowRule,并點(diǎn)擊流控按鈕
-
單機(jī)閾值填入 10,點(diǎn)擊新增
-
新增后會(huì)跳轉(zhuǎn)到流程規(guī)則頁面
-
運(yùn)行jemter,可以看到失敗率為90%,這代表你的限流成功了
上面說了失敗是因?yàn)閿嘌詻]有成功,也就是說沒有返回 success,那他現(xiàn)在返回的是什么呢?
-
察看結(jié)果樹,隨便找一條紅包的記錄,看響應(yīng)數(shù)量
可以看到返回的是 "Blocked by Sentinel (flow limiting)",這個(gè)就是集成后配置的限流頁面的返回值,可以通過 spring.cloud.sentinel.servlet.block-page 指定成你自己的頁面
-
另外打開 apollo 可以看到,多了一個(gè)規(guī)則,這就是你剛剛添加的限流規(guī)則






















