【Sentinel】sentinel 集成 apollo 最佳實踐
前言
在 sentinel 的控制台設置的規則信息默認都是存在內存當中的。所以無論你是重啟了 sentinel 的客戶端還是 sentinel 的控制台。你所設置的規則都會丟失。如果想要 sentinel 在線上環境使用,要么花錢用阿里雲上的付費版本,要么自己去實現規則的持久化,如果你或你所在的公司不差錢,那么關掉這篇文章,直接用付費版吧,省掉了一大堆坑要踩。或者你是一個特立獨行的人,那么我們接着往下說。
首先說一下寫這篇文章的原因,因為真得在與 apllo 集成時,踩坑踩到懷疑人生。另一點是,找了一大堆關於集成的 apollo 的文章,都清一色的都是仿照官方給的限流規則的 DEMO 做的。 但是 sentinel 規則還有熔斷規則、參數限流、系統限流、黑白名單等很多規則,每個規則還有細節上的不一致,這些都沒有提,還有一些客戶端的坑就更沒有了。踩了這么多坑,有了一點心得與體會,梳理與此,希望能幫助到讀者。
拉取 sentinel 控制台源碼進行修改
因為修改內容過多,本文不會詳述,下面的截圖是所有修改內容,並且因為寫這篇文章時,1.7版本在 master 開發,有大量快照版本。所以是切到當前穩定的 1.6 分支進行修改的。我已經 fork sentinel 到我的 github,下面是修改的內容 地址
修改點1:實現所有規則的拉取與推送接口
添加與實現了所有的規則的 Provider 與 Publisher 的配置拉取的與推送。
修改點2:修改控制台使用的規則操作 api
規則在控制台的操作 controller 進行大量改造。
修改點3:修改 xxEntity
最后一點也是最坑的修改了大量的 xxEntity 類,這些類是規則的實體類,本身沒什么,源碼是直接 json 化保存的,但是用於客戶端集成的 spring-cloud-alibaba 使用了 json 校驗,如果 apollo 保存的 json 與客戶端的實體類有一丁點不一樣就報 convert 0 rules but there are 1 rules from datasource . RuleCLass: FlowRule 。 是不是覺得很摸不着頭腦,這報錯跟 json 格式轉換錯誤有什么,下面是 spring-cloud-alibaba json 轉換的代碼。
寫這段代碼的老哥,把這個異常吃了,並補上了一個 // won't be happen 的注釋,你能理解我當時被這個報錯坑的死去活來,后來發現是這里的問題嗎?后來在 github 上找到兩個同樣的問題問題1、問題2,按照方法把 xxEntity 中用不到的字段全部加上 @JSONField(serialize = false) 解決。
修改點4:抽離配置使得可以在啟動的時傳入
添加的配置在下面
使用修改的控制台版本
- 你可以fork sentinel 官方代碼按上述的自行修改,然后打包
- 你可以拉取我 fork 的 sentinel 代碼下來直接打包
- 你可以使用我已經打好的包 地址
自定義配置
配置名稱 | 是否必填 | 默認值 | 作用 |
---|---|---|---|
env | 否 | DEV | 指定 apollo 使用的環境 |
app.id | 否 | sentinel-apollo | 指定保存 sentinel 規則的 apollo 應用 ID |
cluster.name | 否 | default | 指定保存 sentinel 使用的 apollo 集群 |
namespace.name | 否 | application | 指定保存 sentinel 使用的 apollo 命名空間 |
modify.user | 是 | apollo 控制台顯示的修改人賬號,此賬號務必要有此應用的權限 | |
modify.comment | 否 | modify by sentinel-dashboard | apollo 控制台顯示的修改備注 |
release.user | 是 | apollo 控制台顯示的發布人賬號,此賬號務必要有此應用的權限 | |
release.comment | 否 | release by sentinel-dashboard | apollo 控制台顯示的發布備注 |
apollo.portal.url | 是 | apollo 控制台的地址 | |
apollo.application.token | 是 | 指定保存 sentinel 規則的 apollo 應用 openapi 的 token | |
authority.key.suffix | 否 | authority | 認證規則保存在 apollo 中的 key 的后綴 |
degrade.key.suffix | 否 | degrade | 熔斷規則保存在 apollo 中的 key 的后綴 |
flow.key.suffix | 否 | flow | 限流規則保存在 apollo 中的 key 的后綴 |
param.key.suffix | 否 | param_flow | 參數限流規則保存在 apollo 中的 key 的后綴 |
system.key.suffix | 否 | system | 系統限流規則保存在 apollo 中的 key 的后綴 |
auth.username | 否 | sentinel | sentinel 控制台的登錄用戶名 |
auth.password | 否 | sentinel | sentinel 控制台的登錄密碼 |
server.port | 否 | 8080 | sentinel 控制台的啟動端口 |
Apollo 配置
創建用於保存 sentinel 的項目
- 點擊創建項目按鈕
- 輸入項目信息
- 應用 ID 對應 上面表格中的 app.id
- 應用負責人 對應 上面表格中的 modify.user 和 release.user
- 創建一個公共命名空間
- 點擊右下角添加 Namespace 按鈕
- 創建 Namespace
- 名稱對應上面表格中的 namespace.name,注意名稱是要包含部門名的,這里脫敏了
- 類型一定要選擇 public ,原因后面會說
- 點擊右下角添加 Namespace 按鈕
- 發布 Namespace
- 私有的空間是不能被繼承的,application 沒有用,可以刪除
- 這里的用意是我們獨立出一個單獨的用於保存規則的 apollo 應用,因為是公共的,所以其它apollo 應用可以繼承,這樣對於已經集成 apollo 的項目來說,改動最小
- 注意紅色的提示,我們建的公共空間要首先發布一次,否則 api 無法訪問到
- 私有的空間是不能被繼承的,application 沒有用,可以刪除
- 創建此項目的開放平台授權
- 點擊右上角的開放平台授權管理
- 創建應用
- 第三方應用 ID 就是你上面創建的項目的 appId
- 第三方應用名稱 隨便寫
- 賦權
- token 你點擊創建應用后自動生成的
- 被管理的 Appid 就是你上面創建的項目的 appId
- 授權類型 一定要選 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 {
/**
* 沒有注解通過自適應的限流
* @return
*/
@GetMapping("/flowRule")
@ResponseBody
public String flowRule(){
return "success";
}
/**
* 通過手動注解的限流
* @return
*/
@GetMapping("/flowRuleWithAnno")
@ResponseBody
@SentinelResource("flowRuleWithAnno")
public String flowRuleWithAnno(){
return "success";
}
/**
* 參數限流規則測試
* @param param
* @return
*/
@GetMapping("/paramFlowRule")
@ResponseBody
@SentinelResource("paramFlowRule")
public String paramFlowRule(String param){
return "success";
}
/**
* 熔斷規則測試
* @return
*/
@GetMapping("/degradeRule")
@ResponseBody
@SentinelResource("degradeRule")
public String degradeRule(){
throw new RuntimeException("服務器異常");
}
}
配置 application.yml
apollo:
bootstrap:
enabled: true # 開啟 apollo
meta: xxx # 指定 apollo 注冊地址
app:
id: sentinel-apollo # 指定規則項目在 apollo 的app.id,要與 sentinel 控制台啟動參數一致
spring:
application:
name: study-sentinel-example # 應用名稱,不同項目要唯一,會把他做為規則 Key 的前綴
cloud:
sentinel:
transport:
dashboard: localhost:8989 # sentinel 控制台的地址
datasource:
ds1:
apollo:
namespace-name: {部門名}.sentinel-rule # 保存規則的 apollo 應用的公共 namespace, 要與 sentinel 控制台啟動參數一致
rule-type: flow # 指定該數據源是限流規則
flow-rules-key: ${spring.application.name}-${spring.cloud.sentinel.datasource.ds1.apollo.rule-type} # 指定該規則在 apollo 應用中 key 名稱
ds2:
apollo:
namespace-name: {部門名}.sentinel-rule
rule-type: degrade # 指定該數據源是熔斷規則
flow-rules-key: ${spring.application.name}-${spring.cloud.sentinel.datasource.ds2.apollo.rule-type} # 指定該規則在 apollo 應用中 key 名稱
ds3:
apollo:
namespace-name: {部門名}.sentinel-rule
rule-type: param_flow # 指定該數據源是參數限流規則
flow-rules-key: ${spring.application.name}-${spring.cloud.sentinel.datasource.ds3.apollo.rule-type} # 指定該規則在 apollo 應用中 key 名稱
ds4:
apollo:
namespace-name: {部門名}.sentinel-rule
rule-type: system # 指定該數據源是系統限流規則
flow-rules-key: ${spring.application.name}-${spring.cloud.sentinel.datasource.ds4.apollo.rule-type} # 指定該規則在 apollo 應用中 key 名稱
ds5:
apollo:
namespace-name: {部門名}.sentinel-rule
rule-type: authority # 指定該數據源是認證限流(黑白名單)規則
flow-rules-key: ${spring.application.name}-${spring.cloud.sentinel.datasource.ds5.apollo.rule-type} # 指定該規則在 apollo 應用中 key 名稱
sentinel 控制台配置
- 第一次啟動時 sentinel 是沒有應用數據,只要請求幾次你應用的接口就可以了
- 請求之后可以看到我們的應用在右側列表了
- 首先點擊簇點監控,如果是空白的話說明,接口還沒有被請求過,通過上面提供的 jmeter 腳本,可以快速訪問所有接口,訪問后如下圖所示
- 可以看到除了我們手動通過注解定義的資源名,還多了一些是通過我們的 controller 路徑的資源名,這些都是我們客戶端集成 spring-cloud-starter-alibaba-sentinel 包后,自動適配的。這兩種其實在使用上有區別的
- 自動適配的限流后會返回 Blocked by Sentinel (flow limiting)
- 通過注解的是會拋出 UndeclaredThrowableException 異常,我們可以通過 文章 說的方法轉成我們想的限流異常處理。
- 右邊的操作就是添加各種規則,這里修改后會實時同步到客戶端並同步保存到 apollo
- 可以看到除了我們手動通過注解定義的資源名,還多了一些是通過我們的 controller 路徑的資源名,這些都是我們客戶端集成 spring-cloud-starter-alibaba-sentinel 包后,自動適配的。這兩種其實在使用上有區別的
jmeter 配置
jmeter 是用於測試與驗證規則使用的,因為可以設置線程數,所以可以很好的測試限流情況。測試腳本下載
-
線程組要把線程設置成 100,方便后面的統計,另外為了在一秒內執行完,Ramp-Up 時間設為0
-
請求默認值就是填寫你本地的啟的測試項目的地址
-
xx 規則填寫測試接口地址,參數限流因為要做對照實驗所以寫了兩個
-
查看結果樹可以看到你每次請求的內容與結果
-
可以看到上張圖片內有紅色的有綠色的,紅色說明斷言失敗,綠色說明斷言成功,斷言的內容就是包含 success
-
聚合統計,這個可以統計出100個線程請求后的總體結果,我們只要看 Error% 的失敗率就可以了。圖中可以看到除了熔斷限流,其它限流失敗率是 0
測試步驟
- 在簇點鏈路中找到 /test/flowRule,並點擊流控按鈕
- 單機閾值填入 10,點擊新增
- 新增后會跳轉到流程規則頁面
- 運行jemter,可以看到失敗率為90%,這代表你的限流成功了
- 上面說了失敗是因為斷言沒有成功,也就是說沒有返回 success,那他現在返回的是什么呢?
- 察看結果樹,隨便找一條紅包的記錄,看響應數量
- 可以看到返回的是 "Blocked by Sentinel (flow limiting)",這個就是集成后配置的限流頁面的返回值,可以通過 spring.cloud.sentinel.servlet.block-page 指定成你自己的頁面
- 另外打開 apollo 可以看到,多了一個規則,這就是你剛剛添加的限流規則
歡迎關注我的公眾號「KIWI的碎碎念」,也可以收藏 我的博客