【Sentinel】sentinel 集成 apollo 最佳實踐
前言
??在 sentinel 的控制臺設(shè)置的規(guī)則信息默認(rèn)都是存在內(nèi)存當(dāng)中的檐什。所以無論你是重啟了 sentinel 的客戶端還是 sentinel 的控制臺淑际。你所設(shè)置的規(guī)則都會丟失航夺。如果想要 sentinel 在線上環(huán)境使用,要么花錢用阿里云上的付費版本坝茎,要么自己去實現(xiàn)規(guī)則的持久化拯欧,如果你或你所在的公司不差錢,那么關(guān)掉這篇文章暴拄,直接用付費版吧,省掉了一大堆坑要踩编饺」耘瘢或者你是一個特立獨行的人,那么我們接著往下說透且。
??首先說一下寫這篇文章的原因撕蔼,因為真得在與 apllo 集成時,踩坑踩到懷疑人生秽誊。另一點是鲸沮,找了一大堆關(guān)于集成的 apollo 的文章,都清一色的都是仿照官方給的限流規(guī)則的 DEMO 做的锅论。 但是 sentinel 規(guī)則還有熔斷規(guī)則讼溺、參數(shù)限流、系統(tǒng)限流最易、黑白名單等很多規(guī)則怒坯,每個規(guī)則還有細(xì)節(jié)上的不一致,這些都沒有提耘纱,還有一些客戶端的坑就更沒有了敬肚。踩了這么多坑,有了一點心得與體會束析,梳理與此艳馒,希望能幫助到讀者。
拉取 sentinel 控制臺源碼進(jìn)行修改
??因為修改內(nèi)容過多员寇,本文不會詳述弄慰,下面的截圖是所有修改內(nèi)容,并且因為寫這篇文章時蝶锋,1.7版本在 master 開發(fā)陆爽,有大量快照版本。所以是切到當(dāng)前穩(wěn)定的 1.6 分支進(jìn)行修改的扳缕。我已經(jīng) fork sentinel 到我的 github慌闭,下面是修改的內(nèi)容 地址
修改點1:實現(xiàn)所有規(guī)則的拉取與推送接口
??添加與實現(xiàn)了所有的規(guī)則的 Provider 與 Publisher 的配置拉取的與推送。
修改點2:修改控制臺使用的規(guī)則操作 api
規(guī)則在控制臺的操作 controller 進(jìn)行大量改造躯舔。
修改點3:修改 xxEntity
??最后一點也是最坑的修改了大量的 xxEntity 類驴剔,這些類是規(guī)則的實體類,本身沒什么粥庄,源碼是直接 json 化保存的丧失,但是用于客戶端集成的 spring-cloud-alibaba 使用了 json 校驗,如果 apollo 保存的 json 與客戶端的實體類有一丁點不一樣就報 convert 0 rules but there are 1 rules from datasource . RuleCLass: FlowRule 惜互。 是不是覺得很摸不著頭腦布讹,這報錯跟 json 格式轉(zhuǎn)換錯誤有什么琳拭,下面是 spring-cloud-alibaba json 轉(zhuǎn)換的代碼。
??寫這段代碼的老哥描验,把這個異常吃了白嘁,并補(bǔ)上了一個 // won't be happen 的注釋,你能理解我當(dāng)時被這個報錯坑的死去活來挠乳,后來發(fā)現(xiàn)是這里的問題嗎权薯?后來在 github 上找到兩個同樣的問題問題1、問題2睡扬,按照方法把 xxEntity 中用不到的字段全部加上 @JSONField(serialize = false) 解決盟蚣。
修改點4:抽離配置使得可以在啟動的時傳入
??添加的配置在下面
使用修改的控制臺版本
- 你可以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 控制臺顯示的修改人賬號卖怜,此賬號務(wù)必要有此應(yīng)用的權(quán)限 | |
modify.comment | 否 | modify by sentinel-dashboard | apollo 控制臺顯示的修改備注 |
release.user | 是 | apollo 控制臺顯示的發(fā)布人賬號屎开,此賬號務(wù)必要有此應(yīng)用的權(quán)限 | |
release.comment | 否 | release by sentinel-dashboard | apollo 控制臺顯示的發(fā)布備注 |
apollo.portal.url | 是 | apollo 控制臺的地址 | |
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 控制臺的登錄用戶名 |
auth.password | 否 | sentinel | sentinel 控制臺的登錄密碼 |
server.port | 否 | 8080 | sentinel 控制臺的啟動端口 |
Apollo 配置
創(chuàng)建用于保存 sentinel 的項目
-
點擊創(chuàng)建項目按鈕
-
輸入項目信息
- 應(yīng)用 ID 對應(yīng) 上面表格中的 app.id
- 應(yīng)用負(fù)責(zé)人 對應(yīng) 上面表格中的 modify.user 和 release.user
- 創(chuàng)建一個公共命名空間
-
點擊右下角添加 Namespace 按鈕
-
創(chuàng)建 Namespace
- 名稱對應(yīng)上面表格中的 namespace.name,注意名稱是要包含部門名的马靠,這里脫敏了
- 類型一定要選擇 public 奄抽,原因后面會說
-
-
發(fā)布 Namespace
- 私有的空間是不能被繼承的,application 沒有用甩鳄,可以刪除
- 這里的用意是我們獨立出一個單獨的用于保存規(guī)則的 apollo 應(yīng)用逞度,因為是公共的,所以其它apollo 應(yīng)用可以繼承妙啃,這樣對于已經(jīng)集成 apollo 的項目來說档泽,改動最小
- 注意紅色的提示,我們建的公共空間要首先發(fā)布一次揖赴,否則 api 無法訪問到
- 私有的空間是不能被繼承的,application 沒有用甩鳄,可以刪除
- 創(chuàng)建此項目的開放平臺授權(quán)
- 點擊右上角的開放平臺授權(quán)管理
-
創(chuàng)建應(yīng)用
- 第三方應(yīng)用 ID 就是你上面創(chuàng)建的項目的 appId
- 第三方應(yīng)用名稱 隨便寫
-
賦權(quán)
- token 你點擊創(chuàng)建應(yīng)用后自動生成的
- 被管理的 Appid 就是你上面創(chuàng)建的項目的 appId
- 授權(quán)類型 一定要選 App
- 點擊右上角的開放平臺授權(quán)管理
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";
}
/**
* 通過手動注解的限流
* @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ī)則項目在 apollo 的app.id代赁,要與 sentinel 控制臺啟動參數(shù)一致
spring:
application:
name: study-sentinel-example # 應(yīng)用名稱职恳,不同項目要唯一,會把他做為規(guī)則 Key 的前綴
cloud:
sentinel:
transport:
dashboard: localhost:8989 # sentinel 控制臺的地址
datasource:
ds1:
apollo:
namespace-name: {部門名}.sentinel-rule # 保存規(guī)則的 apollo 應(yīng)用的公共 namespace危虱, 要與 sentinel 控制臺啟動參數(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 控制臺配置
-
第一次啟動時 sentinel 是沒有應(yīng)用數(shù)據(jù)鸳吸,只要請求幾次你應(yīng)用的接口就可以了
-
請求之后可以看到我們的應(yīng)用在右側(cè)列表了
-
首先點擊簇點監(jiān)控林束,如果是空白的話說明惹恃,接口還沒有被請求過庇茫,通過上面提供的 jmeter 腳本,可以快速訪問所有接口搀菩,訪問后如下圖所示
- 可以看到除了我們手動通過注解定義的資源名焊虏,還多了一些是通過我們的 controller 路徑的資源名,這些都是我們客戶端集成 spring-cloud-starter-alibaba-sentinel 包后秕磷,自動適配的。這兩種其實在使用上有區(qū)別的
- 自動適配的限流后會返回 Blocked by Sentinel (flow limiting)
- 通過注解的是會拋出 UndeclaredThrowableException 異常炼团,我們可以通過 文章 說的方法轉(zhuǎn)成我們想的限流異常處理澎嚣。
- 右邊的操作就是添加各種規(guī)則疏尿,這里修改后會實時同步到客戶端并同步保存到 apollo
- 可以看到除了我們手動通過注解定義的資源名焊虏,還多了一些是通過我們的 controller 路徑的資源名,這些都是我們客戶端集成 spring-cloud-starter-alibaba-sentinel 包后秕磷,自動適配的。這兩種其實在使用上有區(qū)別的
jmeter 配置
??jmeter 是用于測試與驗證規(guī)則使用的,因為可以設(shè)置線程數(shù)易桃,所以可以很好的測試限流情況褥琐。測試腳本下載
-
線程組要把線程設(shè)置成 100,方便后面的統(tǒng)計晤郑,另外為了在一秒內(nèi)執(zhí)行完敌呈,Ramp-Up 時間設(shè)為0
-
請求默認(rèn)值就是填寫你本地的啟的測試項目的地址
-
xx 規(guī)則填寫測試接口地址,參數(shù)限流因為要做對照實驗所以寫了兩個
-
查看結(jié)果樹可以看到你每次請求的內(nèi)容與結(jié)果
-
可以看到上張圖片內(nèi)有紅色的有綠色的造寝,紅色說明斷言失敗磕洪,綠色說明斷言成功,斷言的內(nèi)容就是包含 success
-
聚合統(tǒng)計诫龙,這個可以統(tǒng)計出100個線程請求后的總體結(jié)果析显,我們只要看 Error% 的失敗率就可以了。圖中可以看到除了熔斷限流签赃,其它限流失敗率是 0
測試步驟
- 在簇點鏈路中找到 /test/flowRule谷异,并點擊流控按鈕
-
單機(jī)閾值填入 10,點擊新增
-
新增后會跳轉(zhuǎn)到流程規(guī)則頁面
-
運行jemter锦聊,可以看到失敗率為90%歹嘹,這代表你的限流成功了
- 上面說了失敗是因為斷言沒有成功,也就是說沒有返回 success孔庭,那他現(xiàn)在返回的是什么呢尺上?
-
察看結(jié)果樹,隨便找一條紅包的記錄史飞,看響應(yīng)數(shù)量
- 可以看到返回的是 "Blocked by Sentinel (flow limiting)"尖昏,這個就是集成后配置的限流頁面的返回值,可以通過 spring.cloud.sentinel.servlet.block-page 指定成你自己的頁面
-
另外打開 apollo 可以看到构资,多了一個規(guī)則抽诉,這就是你剛剛添加的限流規(guī)則