原文:https://www.cnblogs.com/kiwifly/p/11569190.html
【Sentinel】sentinel 集成 apollo 最佳實(shí)踐
前言
在 sentinel 的控制臺(tái)設(shè)置的規(guī)則信息默認(rèn)都是存在內(nèi)存當(dāng)中的铐伴。所以無(wú)論你是重啟了 sentinel 的客戶端還是 sentinel 的控制臺(tái)伦吠。你所設(shè)置的規(guī)則都會(huì)丟失亡电。如果想要 sentinel 在線上環(huán)境使用疾瓮,要么花錢用阿里云上的付費(fèi)版本,要么自己去實(shí)現(xiàn)規(guī)則的持久化,如果你或你所在的公司不差錢,那么關(guān)掉這篇文章菲嘴,直接用付費(fèi)版吧,省掉了一大堆坑要踩汰翠×淦海或者你是一個(gè)特立獨(dú)行的人,那么我們接著往下說(shuō)复唤。
??首先說(shuō)一下寫這篇文章的原因健田,因?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)容過(guò)多衣形,本文不會(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ò)坑的死去活來(lái)边苹,后來(lái)發(fā)現(xiàn)是這里的問(wèn)題嗎?后來(lái)在 github 上找到兩個(gè)同樣的問(wèn)題問(wèn)題1裁僧、問(wèn)題2勾给,按照方法把 xxEntity 中用不到的字段全部加上 @JSONField(serialize = false) 解決。
修改點(diǎn)4:抽離配置使得可以在啟動(dòng)的時(shí)傳入
添加的配置在下面
使用修改的控制臺(tái)版本
- 你可以fork sentinel 官方代碼按上述的自行修改锅知,然后打包
- 你可以拉取我 fork 的 sentinel 代碼下來(lái)直接打包
- 你可以使用我已經(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)顯示的修改人賬號(hào)播急,此賬號(hào)務(wù)必要有此應(yīng)用的權(quán)限 | |
modify.comment | 否 | modify by sentinel-dashboard | apollo 控制臺(tái)顯示的修改備注 |
release.user | 是 | apollo 控制臺(tái)顯示的發(fā)布人賬號(hào),此賬號(hào)務(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 對(duì)應(yīng) 上面表格中的 app.id
- 應(yīng)用負(fù)責(zé)人 對(duì)應(yīng) 上面表格中的 modify.user 和 release.user
-
創(chuàng)建一個(gè)公共命名空間
-
點(diǎn)擊右下角添加 Namespace 按鈕
-
創(chuàng)建 Namespace
- 名稱對(duì)應(yīng)上面表格中的 namespace.name售睹,注意名稱是要包含部門名的桩警,這里脫敏了
- 類型一定要選擇 public ,原因后面會(huì)說(shuō)
-
-
發(fā)布 Namespace
- 私有的空間是不能被繼承的昌妹,application 沒有用捶枢,可以刪除
- 這里的用意是我們獨(dú)立出一個(gè)單獨(dú)的用于保存規(guī)則的 apollo 應(yīng)用,因?yàn)槭枪驳姆裳拢云渌黙pollo 應(yīng)用可以繼承烂叔,這樣對(duì)于已經(jīng)集成 apollo 的項(xiàng)目來(lái)說(shuō),改動(dòng)最小
- 注意紅色的提示固歪,我們建的公共空間要首先發(fā)布一次蒜鸡,否則 api 無(wú)法訪問(wèn)到
- 私有的空間是不能被繼承的昌妹,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>
測(cè)試 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 {
/**
* 沒有注解通過(guò)自適應(yīng)的限流
* @return
*/
@GetMapping("/flowRule")
@ResponseBody
public String flowRule(){
return "success";
}
/**
* 通過(guò)手動(dòng)注解的限流
* @return
*/
@GetMapping("/flowRuleWithAnno")
@ResponseBody
@SentinelResource("flowRuleWithAnno")
public String flowRuleWithAnno(){
return "success";
}
/**
* 參數(shù)限流規(guī)則測(cè)試
* @param param
* @return
*/
@GetMapping("/paramFlowRule")
@ResponseBody
@SentinelResource("paramFlowRule")
public String paramFlowRule(String param){
return "success";
}
/**
* 熔斷規(guī)則測(cè)試
* @return
*/
@GetMapping("/degradeRule")
@ResponseBody
@SentinelResource("degradeRule")
public String degradeRule(){
throw new RuntimeException("服務(wù)器異常");
}
}
配置 application.yml
apollo:
bootstrap:
enabled: true # 開啟 apollo
meta: xxx # 指定 apollo 注冊(cè)地址
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ù)蒲讯,只要請(qǐng)求幾次你應(yīng)用的接口就可以了
-
請(qǐng)求之后可以看到我們的應(yīng)用在右側(cè)列表了
-
首先點(diǎn)擊簇點(diǎn)監(jiān)控忘朝,如果是空白的話說(shuō)明,接口還沒有被請(qǐng)求過(guò)判帮,通過(guò)上面提供的 jmeter 腳本局嘁,可以快速訪問(wèn)所有接口溉箕,訪問(wèn)后如下圖所示
- 可以看到除了我們手動(dòng)通過(guò)注解定義的資源名,還多了一些是通過(guò)我們的 controller 路徑的資源名悦昵,這些都是我們客戶端集成 spring-cloud-starter-alibaba-sentinel 包后约巷,自動(dòng)適配的。這兩種其實(shí)在使用上有區(qū)別的
- 自動(dòng)適配的限流后會(huì)返回 Blocked by Sentinel (flow limiting)
- 通過(guò)注解的是會(huì)拋出 UndeclaredThrowableException 異常旱捧,我們可以通過(guò) 文章 說(shuō)的方法轉(zhuǎn)成我們想的限流異常處理独郎。
- 右邊的操作就是添加各種規(guī)則,這里修改后會(huì)實(shí)時(shí)同步到客戶端并同步保存到 apollo
- 可以看到除了我們手動(dòng)通過(guò)注解定義的資源名,還多了一些是通過(guò)我們的 controller 路徑的資源名悦昵,這些都是我們客戶端集成 spring-cloud-starter-alibaba-sentinel 包后约巷,自動(dòng)適配的。這兩種其實(shí)在使用上有區(qū)別的
jmeter 配置
jmeter 是用于測(cè)試與驗(yàn)證規(guī)則使用的枚赡,因?yàn)榭梢栽O(shè)置線程數(shù)氓癌,所以可以很好的測(cè)試限流情況。測(cè)試腳本下載
-
線程組要把線程設(shè)置成 100贫橙,方便后面的統(tǒng)計(jì)贪婉,另外為了在一秒內(nèi)執(zhí)行完,Ramp-Up 時(shí)間設(shè)為0
-
請(qǐng)求默認(rèn)值就是填寫你本地的啟的測(cè)試項(xiàng)目的地址
-
xx 規(guī)則填寫測(cè)試接口地址卢肃,參數(shù)限流因?yàn)橐鰧?duì)照實(shí)驗(yàn)所以寫了兩個(gè)
-
查看結(jié)果樹可以看到你每次請(qǐng)求的內(nèi)容與結(jié)果
-
可以看到上張圖片內(nèi)有紅色的有綠色的疲迂,紅色說(shuō)明斷言失敗,綠色說(shuō)明斷言成功莫湘,斷言的內(nèi)容就是包含 success
-
聚合統(tǒng)計(jì)尤蒿,這個(gè)可以統(tǒng)計(jì)出100個(gè)線程請(qǐng)求后的總體結(jié)果,我們只要看 Error% 的失敗率就可以了幅垮。圖中可以看到除了熔斷限流腰池,其它限流失敗率是 0
測(cè)試步驟
-
在簇點(diǎn)鏈路中找到 /test/flowRule,并點(diǎn)擊流控按鈕
-
單機(jī)閾值填入 10忙芒,點(diǎn)擊新增
-
新增后會(huì)跳轉(zhuǎn)到流程規(guī)則頁(yè)面
-
運(yùn)行jemter示弓,可以看到失敗率為90%,這代表你的限流成功了
上面說(shuō)了失敗是因?yàn)閿嘌詻]有成功呵萨,也就是說(shuō)沒有返回 success奏属,那他現(xiàn)在返回的是什么呢?
-
察看結(jié)果樹潮峦,隨便找一條紅包的記錄囱皿,看響應(yīng)數(shù)量
可以看到返回的是 "Blocked by Sentinel (flow limiting)",這個(gè)就是集成后配置的限流頁(yè)面的返回值跑杭,可以通過(guò) spring.cloud.sentinel.servlet.block-page 指定成你自己的頁(yè)面
-
另外打開 apollo 可以看到铆帽,多了一個(gè)規(guī)則咆耿,這就是你剛剛添加的限流規(guī)則