1、前言
這是《spring Cloud 進階》專欄的第五篇文章,這篇文章介紹一下阿裡開源的流量防衛兵Sentinel,一款非常優秀的開源項目,經過近10年的雙十一的考驗,非常成熟的一款産品。往期文章如下:
- 五十五張圖告訴你微服務的靈魂擺渡者Nacos究竟有多強?
- openFeign奪命連環9問,這誰受得了?
- 阿裡面試這樣問:Nacos、Apollo、Config配置中心如何選型?這10個次元告訴你!
- 阿裡面試敗北:5種微服務注冊中心如何選型?這幾個次元告訴你!
文章目錄如下:

2、什麼是sentinel?
sentinel顧名思義:衛兵;在Redis中叫做哨兵,用于監控主從切換,但是在微服務中叫做流量防衛兵。
Sentinel 以流量為切入點,從流量控制、熔斷降級、系統負載保護等多個次元保護服務的穩定性。
Sentinel 具有以下特征:
- 豐富的應用場景:Sentinel 承接了阿裡巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量可以承受的範圍)、消息削峰填谷、叢集流量控制、實時熔斷下遊不可用應用等。
- 完備的實時監控:Sentinel 同時提供實時的監控功能。您可以在控制台中看到接入應用的單台機器秒級資料,甚至 500 台以下規模的叢集的彙總運作情況。
- 廣泛的開源生态:Sentinel 提供開箱即用的與其它開源架構/庫的整合子產品,例如與 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您隻需要引入相應的依賴并進行簡單的配置即可快速地接入 Sentinel。同時 Sentinel 提供 Java/Go/C++ 等多語言的原生實作。
- 完善的 SPI 擴充機制:Sentinel 提供簡單易用、完善的 SPI 擴充接口。您可以通過實作擴充接口來快速地定制邏輯。例如定制規則管理、适配動态資料源等。
Sentinel 的主要特性如下圖:
Sentinel 分為兩個部分:
- 核心庫(Java 用戶端)不依賴任何架構/庫,能夠運作于所有 Java 運作時環境,同時對 Dubbo / Spring Cloud 等架構也有較好的支援。
- 控制台(Dashboard)基于 Spring Boot 開發,打包後可以直接運作,不需要額外的 Tomcat 等應用容器。
總之一句話:sentinel真牛逼,完爆Hystrix.........
3、sentinel和Hystrix有何差別?
不多說了,總之一句話:Hystrix趕緊放棄,用sentinel......
具體差別如下圖:
4、sentinel版本如何選擇?
由于陳某寫的是Spring Cloud 進階一個系列,使用的聚合項目,是以版本還是保持和之前文章一樣,不清楚的可以看這篇:五十五張圖告訴你微服務的靈魂擺渡者Nacos究竟有多強?
這裡選擇的
spring-cloud-alibaba-dependencies
的版本是
2.2.1.RELEASE
,是以sentinel版本選擇
1.7.1
,大家可以根據自己的版本選擇對應sentinel的版本,版本對應關系如下圖:
注意:一定要按照官方推薦的版本适配,否則出現意想不到的BUG追悔莫及.........
5、Sentinel 控制台如何安裝?
sentinel和nacos一樣,都有一個控制台,但是這裡不用自己手動搭建一個微服務,官方已經搭建好了,隻需要下載下傳對應得jar包運作即可。下載下傳位址:https://github.com/alibaba/Sentinel/tags
選擇對應得版本下載下傳即可,我這裡選擇
1.7.1
版本,下載下傳的jar包如下圖:
當然你可以通過源碼建構:mvn clean package
注意:JDK版本必須
>=1.8
此時我們隻需要運作這個jar包即可,指令如下:
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.1.jar
上述參數含義如下:
-
:指定啟動的端口,預設-Dserver.port
8080
-
:指定本服務的名稱-Dproject.name
-
:指定sentinel控制台的位址,用于将自己注冊進入實作監控自己-Dcsp.sentinel.dashboard.server
啟動成功之後,浏覽器通路:http://localhost:8080,登入頁面如下圖:
預設的使用者名和密碼:sentinel/sentinel
登入成功之後頁面如下:
可以看到目前隻有一個服務
sentinel-dashboard
被監控了,這個服務就是自己。
注意:上述參數都是可選的,沒必要可以不填。
那麼問題來了:預設的使用者名和密碼在生産環境上肯定不能用,如何修改呢?
從 Sentinel 1.6.0 起sentinel已經支援自定義使用者名和密碼了,隻需要在執行jar指令時指定即可,指令如下:
java -Dsentinel.dashboard.auth.username=admin -Dsentinel.dashboard.auth.password=123 -jar sentinel-dashboard-1.7.1.jar
使用者可以通過如下參數進行配置:
-
用于指定控制台的登入使用者名為-Dsentinel.dashboard.auth.username=sentinel
;sentinel
-
用于指定控制台的登入密碼為-Dsentinel.dashboard.auth.password=123456
;如果省略這兩個參數,預設使用者和密碼均為123456
sentinel
-
用于指定 Spring Boot 服務端 session 的過期時間,如-Dserver.servlet.session.timeout=7200
表示 7200 秒;7200
表示 60 分鐘,預設為 30 分鐘;60m
注意:部署多台控制台時,session 預設不會在各執行個體之間共享,這一塊需要自行改造。
除了使用者名密碼相關的配置,sentinel控制台還提供了其他的可配置選項,如下圖:
6、微服務如何接入sentinel控制台?
微服務為什麼要內建sentinel控制台,sentinel不是提供了相關的API嗎?
其實Spring Boot 官方一直提倡約定>配置>編碼的規則,能夠不寫死何樂而不為呢?
是以本文後續内容主要還是結合sentinel控制台進行講解,關于API的使用大家可以按照官方文檔學習,講解的非常清楚。
好了,言歸正傳,微服務如何接入sentinel控制台呢?
1、建立微服務子產品注冊進入Nacos
這裡的注冊中心依然使用的是nacos,有不會用的請看專欄第一篇Nacos文章:五十五張圖告訴你微服務的靈魂擺渡者Nacos究竟有多強?
建立一個微服務子產品:sentinel-service9008,相關代碼不貼出了。
相關配置如下:
server: port: 9008 spring: application: ## 指定服務名稱,在nacos中的名字 name: sentinel-service cloud: nacos: discovery: # nacos的服務位址,nacos-server中IP位址:端口号 server-addr: 127.0.0.1:8848 management: endpoints: web: exposure: ## yml檔案中存在特殊字元,必須用單引号包含,否則啟動報錯 include: '*'
源碼全部會上傳,擷取方式看文末!
2、添加依賴
除了Nacos的依賴,還需要添加一個sentinel的依賴:
<!--sentinel的依賴--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
以上隻貼出了sentinel相關依賴,nacos依賴不再貼了,見源碼!
3、添加配置內建控制台
隻需要添加如下配置即可內建sentinel控制台:
spring: cloud: sentinel: transport: ## 指定控制台的位址,預設端口8080 dashboard: localhost:8080
4、建立一個測試接口
下面建立一個測試接口,用于測試相關規則,如下:
@RestController @RequestMapping("/sentinel") public class FlowLimitController { @GetMapping("/test") public String test(){ return "接收到一條消息--------"; } }
5、啟動微服務
啟動9008這個微服務,然後浏覽器輸入:
http://localhost:9008/sentinel/test
,此時檢視sentinel控制台,将會看見sentinel-service這個服務已經被監控了,如下圖:
注意:sentinel是懶加載機制,隻有通路過一次的資源才會被監控。
不過可以通過配置關閉懶加載,在項目啟動時就連接配接sentinel控制台,配置如下:
spring: sentinel: # 取消控制台懶加載,項目啟動即連接配接Sentinel eager: true
7、流量控制如何配置?
流量控制(flow control),其原理是監控應用流量的 QPS 或并發線程數等名額,當達到指定的門檻值時對流量進行控制,以避免被瞬時的流量高峰沖垮,進而保障應用的高可用性。
QPS:每秒請求數,即在不斷向伺服器發送請求的情況下,伺服器每秒能夠處理的請求數量。
并發線程數:指的是施壓機施加的同時請求的線程數量。
同一個資源可以建立多條限流規則,一條限流規則由以下元素組成:
- resource:資源名,即限流規則的作用對象。
- count: 限流門檻值
- grade:限流門檻值類型(1:QPS 0:并發線程數),預設值QPS
- limitApp:流控針對的調用來源,若為
則不區分調用來源,預設值defaultdefault
- strategy:判斷的根據是資源自身(0),還是根據其它關聯資源 (1),還是根據鍊路入口(2),預設值根據資源本身。
- controlBehavior: 流控效果(直接拒絕(0) / 排隊等待(2) / 預熱冷啟動(1)),預設值直接拒絕。
以上元素限流元素對應的類是
com.alibaba.csp.sentinel.slots.block.flow.FlowRule
,各元素如下圖:
注意:各個元素的取值以及預設值一定要記住,後續配置将會用到。
以上幾個元素在sentinel控制台對應規則如下圖:
1、三種流控效果
流控效果總共分為三種,對應元素
controlBehavior
,分别如下:
快速失敗
預設的流量控制方式,當QPS超過任意規則的門檻值後,新的請求就會被立即拒絕,拒絕方式為抛出
FlowException
。
warm up
即預熱/冷啟動方式。當系統長期處于低水位的情況下,當流量突然增加時,直接把系統拉升到高水位可能瞬間把系統壓垮。通過"冷啟動",讓通過的流量緩慢增加,在一定時間内逐漸增加到門檻值上限,給冷系統一個預熱的時間,避免冷系統被壓垮。
注意:這一效果隻針對QPS流控,并發線程數流控不支援。
預熱底層是根據令牌桶算法實作的,源碼對應得類在
com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController
算法中有一個冷卻因子,預設值是3,即請求 QPS 從
coldFactor
開始,經預熱時長逐漸升至設定的 QPS 門檻值。
threshold(門檻值) / 3
比如設定QPS門檻值為3,流控效果為warm up,預熱時長為5秒,如下圖:
這樣配置之後有什麼效果呢:QPS起初會從(3/3/=1)每秒通過一次請求開始預熱直到5秒之後達到每秒通過3次請求。動态效果圖如下:
從上述動畫可以清楚的看見:前幾秒是頻繁流控的,直到5秒,QPS門檻值達到了3。
具體算法原理請看:https://github.com/alibaba/Sentinel/wiki/限流---冷啟動
排隊等待
勻速排隊方式會嚴格控制請求通過的間隔時間,也即是讓請求以均勻的速度通過,對應的是漏桶算法。源碼對應得類:
com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController
簡單舉個栗子:你去大學食堂吃飯,隻有一個阿姨在打飯,那麼所有人都要排隊打飯,每次隻有一個人打到飯,其他人都在排隊等待。
不同的是sentinel有個逾時等待時間,一旦超過這個預定設定的時間将會被限流。
該方式作用如下圖:
這種方式适合用于請求以突刺狀來到,這個時候我們不希望一下子把所有的請求都通過,這樣可能會把系統壓垮;同時我們也期待系統以穩定的速度,逐漸處理這些請求,以起到“削峰填谷”的效果,而不是拒絕所有請求。
比如設定QPS門檻值為1,逾時等待時間為10000毫秒,如下圖:
此時的效果如下:
從上圖可以看到:連續點選重新整理請求,雖然設定了QPS門檻值為1,但是并沒有被限流,而是在等待,因為設定了逾時等待時間為10秒。
具體算法原理請看:https://github.com/alibaba/Sentinel/wiki/流量控制-勻速排隊模式
2、三種流控模式
流控模式總共分為三種,對應元素
strategy
- 直接拒絕:接口達到限流條件時,直接限流
- 關聯:當關聯的資源達到門檻值時,就限流自己
- 鍊路:隻記錄指定鍊路上的流量(指定資源從入口資源進來的流量,如果達到門檻值,就可以限流)
下面來詳細介紹下以上三種流控模式。
直接拒絕
顧名思義:預設的流量控制方式,當QPS超過任意規則的門檻值後,新的請求就會被立即拒絕,拒絕方式為抛出
FlowException
。上面的幾個例子都是配置了直接拒絕這個模式,這裡不再詳細介紹。
關聯
典型的使用場景:一個是支付接口,一個是下單接口,此時一旦支付接口達到了門檻值,那麼訂單接口就應該被限流,不然這邊還在下單,消費者等待或者直接被拒絕支付将會極大的影響使用者體驗。
簡而言之:A關聯B,一旦B達到門檻值,則A被限流
示範一下效果,建立以下兩個接口:
@RestController @RequestMapping("/sentinel") public class FlowLimitController { /** * 下單接口 * @return */ @GetMapping("/order") public String order() { return "下單成功.........."; } /** * 支付接口 * @return */ @GetMapping("/pay") public String pay() { return "支付成功.........."; } }
此時的流控規則配置如下圖:
注意:關聯之後,這裡設定的限流規則是對被關聯資源,也就是
/sentinel/pay
這個資源,但是真正被限流則是
/sentinel/order
如何示範效果呢?很簡單,隻需要不斷的請求
/sentinel/pay
達到門檻值,然後在請求
/sentinel/order
利用POSTMAN不斷向
/sentinel/pay
送出請求,然後浏覽器請求
/sentinel/order
,結果如下圖:
可以看到訂單接口被限流了.............
3、兩種統計類型
流控分為兩種統計類型,分别是QPS,并發線程數,很多人不太明白這兩種統計類型有什麼差別?
舉個栗子:陳某帶了一個億去銀行存錢,但是銀行大門保安要查健康碼,每秒最多隻能同時進入4個人,并且銀行中隻有兩個從業人員工作,如下圖:
此時的QPS含義:從保安到銀行這一段,即是保安放行進入銀行的人數。
此時并發線程數的含義:銀行隻有兩個從業人員在工作,那麼最多隻能同時處理兩個任務,這裡并發線程數的門檻值就是2。
8、降級規則如何配置?
熔斷降級在日常生活中也是比較常見的,場景如下:
- 股票市場的熔斷,當價格觸發到了熔點之後,會暫停交易一段時間,或者交易可以繼續進行,但是報價會限制在一定的範圍。
- 電壓過高導緻保險絲觸發熔斷保護
在大型的分布式系統中,一個請求的依賴如下圖:
如果這個時候,某個服務出現一些異常,比如:
- 服務提供者不可用(硬體故障、程式bug、網絡故障、使用者請求量較大)
- 重試導緻的流量過大
- 服務調用者使用同步調用,産生大量的等待線程占用系統資源,一旦線程資源被耗盡,調用者提供的服務也會變成不可用狀态
那麼将會導緻整個服務不可用,用古話來講就是:千裡之堤毀于蟻穴。
所謂程式設計源于生活,架構師們根據生活的經驗設計出了服務的熔斷降級政策,很好的解決了這類問題。
熔斷降級規則對應sentinel控制台的降級規則這一欄,如下圖:
熔斷降級涉及到的幾個屬性如下表:
源碼中對應得類為:
com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule
三種熔斷政策
Sentinel 提供以下幾種熔斷政策:
- 平均響應時間 (
):當 1s 内持續進入 5 個請求,對應時刻的平均響應時間(秒級)均超過門檻值(DEGRADE_GRADE_RT
,以 ms 為機關),那麼在接下的時間視窗(count
中的DegradeRule
,以 s 為機關)之内,對這個方法的調用都會自動地熔斷(抛出timeWindow
)。注意 Sentinel 預設統計的 RT 上限是 4900 ms,超出此門檻值的都會算作 4900 ms,若需要變更此上限可以通過啟動配置項DegradeException
來配置。-Dcsp.sentinel.statistic.max.rt=xxx
- 異常比例 (
):當資源的每秒請求量 >= 5,并且每秒異常總數占通過量的比值超過門檻值(DEGRADE_GRADE_EXCEPTION_RATIO
DegradeRule
)之後,資源進入降級狀态,即在接下的時間視窗(count
DegradeRule
,以 s 為機關)之内,對這個方法的調用都會自動地傳回。異常比率的門檻值範圍是timeWindow
,代表 0% - 100%。[0.0, 1.0]
- 異常數 (
):當資源近 1 分鐘的異常數目超過門檻值之後會進行熔斷。注意由于統計時間視窗是分鐘級别的,若DEGRADE_GRADE_EXCEPTION_COUNT
小于 60s,則結束熔斷狀态後仍可能再進入熔斷狀态。timeWindow
下面示範一個平均響應時間熔斷,建立一個接口,如下:
@RestController @RequestMapping("/sentinel/provider") @Slf4j public class FlowLimitController { @GetMapping("/test") public String test() throws InterruptedException { //休眠3秒鐘 Thread.sleep(3000); log.info("收到一條消息----test"); return "接收到一條消息--------"; } }
在控台為這個接口設定平均響應時間為200毫秒,時間視窗為1秒,大緻意思:平均的響應時間大于200毫秒之後,在接下來的1秒時間内将會直接熔斷,如下圖:
使用Jmeter開啟10個線程循環跑,然後在浏覽器中通路這個接口,傳回結果如下圖:
為什麼呢?由于的接口中休眠了3秒,平均響應時間肯定大于200毫秒,是以直接被熔斷了。
注意:這裡熔斷後直接傳回預設的資訊,後面會介紹如何定制熔斷傳回資訊。
9、熱點參數如何限流?
顧名思義:熱點就是經常通路的資料,很多時候肯定是希望統計某個通路頻次
Top K
資料并對其進行限流。
比如秒殺系統中的商品ID,對于熱點商品那一瞬間的并發量是非常可怕的,是以必須要對其進行限流。
Sentinel 利用 LRU 政策統計最近最常通路的熱點參數,結合令牌桶算法來進行參數級别的流控。
注意:熱點參數限流隻針對QPS。
官方文檔:https://github.com/alibaba/Sentinel/wiki/熱點參數限流
概念了解了,來看下sentinel控制台如何設定熱點參數限流,如下圖:
規則對應得源碼在
com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule
這個類中,各種屬性含義如下圖:
規則都懂了,下面我們通過實戰來示範一下熱點參數到底是如何限流的。
注意:熱點參數限流隻作用于八大基本類型。
1、建立一個資源
現在先建立一個service,用
@SentinelResource
這個注解定義一個資源,這個注解後續将會詳細介紹,先忽略,代碼如下:
@Service @Slf4j public class FlowServiceImpl implements FlowService { /** * @SentinelResource的value屬性指定了資源名,一定要唯一 * blockHandler屬性指定了兜底方法 */ @Override @SentinelResource(value = "OrderQuery",blockHandler = "handlerQuery") public String query(String p1, String p2) { log.info("查詢商品,p1:{},p2:{}",p1,p2); return "查詢商品:success"; } /** * 對應得兜底方法,一旦被限流将會調用這個方法來處理 */ public String handlerQuery(@RequestParam(value = "p1",required = false) String p1, @RequestParam(value = "p2",required = false)String p2, BlockException exception){ log.info("查詢商品,p1:{},p2:{}",p1,p2); return "查詢商品:熔斷了......"; } }
上述代碼什麼意思呢?如下:
- 如果query這個接口沒有被限流則傳回:查詢商品:success
- 如果query這個接口被限流了,則進入了兜底方法
方法,傳回:查詢商品:熔斷了......handlerQuery
2、建立controller接口
下面建立一個controller進行測試,代碼如下:
@RestController @RequestMapping("/sentinel/provider") @Slf4j public class FlowLimitController { @Autowired private FlowService flowService; @GetMapping("/order/query") public String query(@RequestParam(value = "p1",required = false) String p1, @RequestParam(value = "p2",required = false)String p2){ return flowService.query(p1,p2); } }
可以看到接口中有兩個參數,分别是
p1
、
p2
3、添加熱點參數限流規則
在sentinel控制台點選
熱點規則->新增熱點限流規則
,添加如下圖規則:
上述配置的具體含義:當
OrderQuery
這個資源中的第0個參數QPS超過1秒1次将會被限流。這裡參數索引是從0開始,第0個就是對應接口中的
p1
這個參數。
第一個測試:浏覽器直接通路:http://localhost:9009/sentinel/provider/order/query?p1=22&p2=1222,連續點選将會看到這個接口被熔斷降級了,如下圖:
這也正是驗證了上述的熱點參數限流配置。
第二個測試:浏覽器輸入:http://localhost:9009/sentinel/provider/order/query?p2=1222,連續點選将會看到這個接口并沒有被熔斷降級,如下圖:
注意:對于熱點參數限流,隻有包含指定索引的參數請求才會被限流,否則不影響。
此時産品說:ID為100的這個産品點選量太少了,你們趕緊調整下這個商品的限流規則。這個時候該怎麼辦呢?
别着急,sentinel顯然考慮到了這一點,提供了參數例外項這項配置,針對産品需求配置如下:
從上圖配置中,我們将參數值p1這個參數值等于100的時候,限流門檻值設定成了100,也就是說
p1=100
這個請求QPS放寬到1秒請求100次以上才會被限流。
驗證:浏覽器輸入位址:http://localhost:9009/sentinel/provider/order/query?p1=100,無論點選多麼快,都沒有被熔斷降級,顯然是配置生效了,如下圖:
以上源碼在sentinel-openfeign-provider9009這個子產品中,文末有源碼擷取方式。
10、系統自适應如何限流?
前面熱點參數、普通流量限流都是針對的某個接口,這裡系統自适應限流針對是整個系統的入口流量,從單台機器的 load、CPU 使用率、平均 RT、入口 QPS 和并發線程數等幾個次元監控應用名額,讓系統盡可能跑在最大吞吐量的同時保證系統整體的穩定性。
sentinel控制台對應如下圖:
門檻值類型有五種,分别如下:
- Load 自适應(僅對 Linux/Unix-like 機器生效):系統的 load1 作為啟發名額,進行自适應系統保護。當系統 load1 超過設定的啟發值,且系統目前的并發線程數超過估算的系統容量時才會觸發系統保護(BBR 階段)。系統容量由系統的
估算得出。設定參考值一般是maxQps * minRt
CPU cores * 2.5
- CPU usage(1.5.0+ 版本):當系統 CPU 使用率超過門檻值即觸發系統保護(取值範圍 0.0-1.0),比較靈敏。
- 平均 RT:當單台機器上所有入口流量的平均 RT 達到門檻值即觸發系統保護,機關是毫秒。
- 并發線程數:當單台機器上所有入口流量的并發線程數達到門檻值即觸發系統保護。
- 入口 QPS:當單台機器上所有入口流量的 QPS 達到門檻值即觸發系統保護。
官方文檔:https://github.com/alibaba/Sentinel/wiki/系統自适應限流
系統規則的配置比較簡單,這裡以入口QPS為例進行示範,為了示範真實情況,清掉所有的限流規則,添加系統規則,如下圖:
這個QPS系統規則一配置,該微服務中的所有接口都将會被這個規則限制,比如通路:http://localhost:9009/sentinel/provider/pay,連續點選,如下圖:
可以看到已經被限流了,不僅是這個接口,所有接口都會生效。
注意:系統規則中的入口QPS這個規則不建議配置,一旦配置上了可能導緻整個服務不可用。
11、如何自定義限流傳回的異常資訊?
在前面的例子中,無論是熔斷降級還是被限流傳回的異常資訊都是
Blocked by Sentinel (flow limiting)
,這個是Sentinel預設的異常資訊。
很顯然預設的異常資訊并不能滿足我們的業務需求,是以我們需要根據前後端規則制定自己的異常傳回資訊。
這裡将會用到一個注解
@SentinelResource
,這個在上文也是提到過,這個注解中有兩個關于限流兜底方法的屬性,如下:
- blockHandler: 對應處理
的函數名稱。blockHandler 函數通路範圍需要是BlockException
,傳回類型需要與原方法相比對,參數類型需要和原方法相比對并且最後加一個額外的參數,類型為public
。blockHandler 函數預設需要和原方法在同一個類中。BlockException
- blockHandlerClass:指定
為對應的類的blockHandlerClass
對象,注意對應的函數必需為Class
函數,否則無法解析。static
官方文檔:https://github.com/alibaba/Sentinel/wiki/注解支援
使用
@SentinelResource
注解自定義一個限流異常傳回資訊,先自定義一個資源,指定兜底方法為
handler
,代碼如下:
第二步:寫個對應得兜底方法,必須在同一個類中,代碼如下:
第三步:對資源
QueryOrder
新增一個限流規則,如下圖:
第四步:寫個controller,代碼就不曬了,自己寫吧,哈哈。。。。
第五步:調用接口,瘋狂點選,将會出現兜底方法中定義的傳回資訊,如下圖:
到這兒基本算是成功了,但是有個問題:兜底方法必須要和業務方法放在同一個類中,這樣代碼耦合度不是很高嗎?
@SentinelResource
提供一個屬性
blockHandlerClass
,完美的解決了這一個問題,能夠将兜底方法單獨放在一個類中,下面來介紹一下。
第一步:建立一個單獨的類
CommonHandler
來放置兜底方法,代碼如下:
第二步:在
@SentinelResource
注解中指定blockHandlerClass為上面的類,blockHandler指定兜底方法名,代碼如下:
好了,至此就完成了,自己照着試試吧.......
上述源碼在sentinel-openfeign-provider9009這個子產品中,源碼擷取方式見文末。
12、如何對異常進行降級處理?
程式員每天都在制造BUG,沒有完美的代碼,也沒有完美的程式員,針對代碼的運作時異常我們無法避免,但是我們可以當出現異常的時候進行捕獲并做出相應的處理,我們稱之為降級處理。
異常的降級還是要用到
@SentinelResource
注解,其中相關的幾個屬性如下:
- fallback:fallback 函數名稱,可選項,用于在抛出異常的時候提供 fallback 處理邏輯。fallback 函數可以針對所有類型的異常(除了
裡面排除掉的異常類型)進行處理。fallback 函數簽名和位置要求:exceptionsToIgnore
- 傳回值類型必須與原函數傳回值類型一緻;
- 方法參數清單需要和原函數一緻,或者可以額外多一個
類型的參數用于接收對應的異常。Throwable
- fallback 函數預設需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定
fallbackClass
對象Class
- fallbackClass:指定對應的類的
對象,注意對應的函數必需為 static 函數,否則無法解析。Class
- defaultFallback(since 1.6.0):預設的 fallback 函數名稱,可選項,通常用于通用的 fallback 邏輯(即可以用于很多服務或方法)。預設 fallback 函數可以針對所有類型的異常(除了
裡面排除掉的異常類型)進行處理。若同時配置了 fallback 和 defaultFallback,則隻有 fallback 會生效。defaultFallback 函數簽名要求:exceptionsToIgnore
- 方法參數清單需要為空,或者可以額外多一個
Throwable
- defaultFallback 函數預設需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定
fallbackClass
Class
- exceptionsToIgnore(since 1.6.0):用于指定哪些異常被排除掉,不會計入異常統計中,也不會進入 fallback 邏輯中,而是會原樣抛出。
1.8.0 版本開始,
defaultFallback
支援在類級别進行配置。
注:1.6.0 之前的版本 fallback 函數隻針對降級異常( DegradeException
)進行處理,不能針對業務異常進行處理。
下面定義一個建立訂單的接口,手動制造一個
1/0
異常,代碼如下:
上述接口并沒有進行異常降級處理,是以調用該接口直接傳回了異常資訊,非常不友好,如下圖:
我們可以使用
fallback
指定異常降級的兜底方法,此時業務方法改造如下:
fallbackClass
屬性指定單獨一個類處理異常降級,降低了代碼的耦合度,
fallback
屬性指定了降級兜底的方法,代碼如下:
此時再次通路接口,雖然有異常,但是傳回的确實降級兜底方法中的傳回資訊,如下圖:
到了這裡基本滿足了異常降級的處理需求,但是仍然有個疑問:能否隻用一個方法處理全部的異常?
答案是:能,必須能,此時就要用到
defaultFallback
這個屬性了,指定預設的降級兜底方法,此時的業務方法變成如下代碼:
defaultFallback
屬性指定了預設的降級兜底方法,這個方法代碼如下:
好了,異常降級處理到這兒已經介紹完了,但是仍然有一個問題:若 blockHandler 和 fallback 都進行了配置,那麼哪個會生效?
結論:若 blockHandler 和 fallback 都進行了配置,則被限流降級而抛出時隻會進入
BlockException
處理邏輯。若未配置
blockHandler
blockHandler
和
fallback
,則被限流降級時會将
defaultFallback
直接抛出。
BlockException
将
createOrder
這個業務接口改造一下,同時指定blockHandler和fallback,代碼如下:
此時不配置任何規則,直接通路接口,可以看到這裡直接進入了異常降級處理,如下圖:
我們對
createOrder
這個資源配置降級規則:60秒内如果出現2個以上的異常直接限流,如下圖:
此時我們再次通路這個接口,可以看到前兩次直接進入了
fallback
指定的方法中(并未達到限流的異常數門檻值),兩次之後就被限流了,進入了
blockHandler
方法中,效果如下圖:
13、sentinel的黑白名單如何設定?
顧名思義,黑名單就是拉黑呗,拉黑就是不能通路了呗,sentinel能夠針對請求來源進行是否放行,若配置白名單則隻有請求來源位于白名單内時才可通過;若配置黑名單則請求來源位于黑名單時不通過,其餘的請求通過。
sentinel控制台對應得規則配置如下圖:
該規則對應得源碼為
com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule
,幾個屬性如下:
-
:資源名,即限流規則的作用對象。resource
-
:對應的黑名單/白名單,不同 origin 用limitApp
分隔,如,
appA,appB
-
:限制模式,strategy
為白名單模式,AUTHORITY_WHITE
為黑名單模式,預設為白名單模式。AUTHORITY_BLACK
官方文檔:https://github.com/alibaba/Sentinel/wiki/黑白名單控制
這裡有個問題:請求來源是什麼,怎麼擷取?
Sentinel提供了一個接口
RequestOriginParser
,我們可以實作這個接口根據自己業務的規則解析出請求來源名稱。
下面我以IP作為區分請求來源,代碼如下:
然後将
127.0.0.1
設定為黑名單,如下圖:
直接通路:http://127.0.0.1:9009/sentinel/rate/order/query?id=1002,結果如下圖:
可以看到被限流了哦.................
好了,黑白名單就介紹到這裡。
14、限流規則如何持久化?
Sentinel預設限流規則是存儲在記憶體中,隻要服務重新開機之後對應得限流規則也會消失,實際的生産中肯定是不允許這種操作,是以限流規則的持久化迫在眉睫。
sentinel官方文檔提供了兩種持久化模式,分别如下:
但是官方推薦使用
Push
模式,下面陳某就Push模式介紹一下持久化限流規則。這裡使用Nacos作為配置中心。
盜用官方一張架構圖,如下:
1、添加依賴
這裡需要添加一個依賴,如下:
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
2、配置檔案中配置相關資訊
既然使用到了Nacos作為配置中心,肯定是要配置相關的位址、dataId...
在
application.yml
配置檔案中添加如下配置:
spring: cloud: sentinel: ## nacos持久化配置 datasource: ## 配置流控規則,名字任意 ds-flow: nacos: ## nacos的位址 server-addr: 127.0.0.1:8848 ## 配置ID dataId: ${spring.application.name}-flow ## 配置分組,預設是DEFAULT_GROUP groupId: DEFAULT_GROUP ## 配置存儲的格式 data-type: json ## rule-type設定對應得規則類型,總共七大類型,在com.alibaba.cloud.sentinel.datasource.RuleType這個枚舉類中有展現 rule-type: flow ## 配置降級規則,名字任意 ds-degrade: nacos: ## nacos的位址 server-addr: 127.0.0.1:8848 ## 配置ID dataId: ${spring.application.name}-degrade ## 配置分組,預設是DEFAULT_GROUP groupId: DEFAULT_GROUP ## 配置存儲的格式 data-type: json ## rule-type設定對應得規則類型,總共七大類型,在com.alibaba.cloud.sentinel.datasource.RuleType這個枚舉類中有展現 rule-type: degrade
上述配置僅僅展示了和持久化相關的一些配置,其他相關的配置代碼就不貼了,稍後自己看源碼。
spring.cloud.sentinel.datasource
下可以配置多個規則,陳某這裡隻配置了限流和降級規則,其他規則自己嘗試配一下,不同規則通過
rule-type
區分,其取值都在
com.alibaba.cloud.sentinel.datasource.RuleType
這個枚舉類中,對應着sentinel中的幾大統計規則。
3、在Nacos添加對應的規則配置
上述配置中對應的限流(flow)規則如下圖:
上述配置中對應的降級(degrade)規則如下圖:
先不糾結JSON資料裡面到底是什麼,先看效果,全部釋出之後,Nacos中總共有了兩個配置,如下圖:
上圖中可以看到我們的兩種規則已經在Nacos配置好了,來看一下sentinel中是否已經生效了,如下圖:
哦了,已經生效了,由于是push模式,隻要nacos中點選釋出配置,相關規則配置就會推送到sentinel中。
伏筆:push模式隻能保證Nacos中的修改推送到sentinel控制台,但是sentinel控制台的限流規則修改如何推送到Nacos呢?别着急,下面将會介紹..............
4、JSON中到底怎麼寫?
很多人好奇JOSN中的配置到底怎麼寫?其實很簡單,陳某在介紹各種規則的時候都明确告訴你每種規則對應源碼中的實作類,比如流控規則對應的類就是
com.alibaba.csp.sentinel.slots.block.flow.FlowRule
,JOSN中各個屬性也是來源于這個類。
下面陳某列出各個規則的JSON配置,開發中照着改即可。
1、流控規則
[ { // 資源名 "resource": "/test", // 針對來源,若為 default 則不區分調用來源 "limitApp": "default", // 限流門檻值類型(1:QPS;0:并發線程數) "grade": 1, // 門檻值 "count": 1, // 是否是叢集模式 "clusterMode": false, // 流控效果(0:快速失敗;1:Warm Up(預熱模式);2:排隊等待) "controlBehavior": 0, // 流控模式(0:直接;1:關聯;2:鍊路) "strategy": 0, // 預熱時間(秒,預熱模式需要此參數) "warmUpPeriodSec": 10, // 逾時時間(排隊等待模式需要此參數) "maxQueueingTimeMs": 500, // 關聯資源、入口資源(關聯、鍊路模式) "refResource": "rrr" } ]
2、降級規則
[ { // 資源名 "resource": "/test1", "limitApp": "default", // 熔斷政策(0:慢調用比例,1:異常比率,2:異常計數) "grade": 0, // 最大RT、比例門檻值、異常數 "count": 200, // 慢調用比例門檻值,僅慢調用比例模式有效(1.8.0 引入) "slowRatioThreshold": 0.2, // 最小請求數 "minRequestAmount": 5, // 當機關統計時長(類中預設1000) "statIntervalMs": 1000, // 熔斷時長 "timeWindow": 10 } ]
3、熱點規則
[ { // 資源名 "resource": "/test1", // 限流模式(QPS 模式,不可更改) "grade": 1, // 參數索引 "paramIdx": 0, // 單機門檻值 "count": 13, // 統計視窗時長 "durationInSec": 6, // 是否叢集 預設false "clusterMode": 預設false, // "burstCount": 0, // 叢集模式配置 "clusterConfig": { // "fallbackToLocalWhenFail": true, // "flowId": 2, // "sampleCount": 10, // "thresholdType": 0, // "windowIntervalMs": 1000 }, // 流控效果(支援快速失敗和勻速排隊模式) "controlBehavior": 0, // "limitApp": "default", // "maxQueueingTimeMs": 0, // 進階選項 "paramFlowItemList": [ { // 參數類型 "classType": "int", // 限流門檻值 "count": 222, // 參數值 "object": "2" } ] } ]
4、系統規則
負值表示沒有門檻值檢查。
不需要删除參數
[ { // RT "avgRt": 1, // CPU 使用率 "highestCpuUsage": -1, // LOAD "highestSystemLoad": -1, // 線程數 "maxThread": -1, // 入口 QPS "qps": -1 } ]
5、授權規則
[ { // 資源名 "resource": "sentinel_spring_web_context", // 流控應用 "limitApp": "/test", // 授權類型(0代表白名單;1代表黑名單。) "strategy": 0 } ]
注意:對于上述JOSN中的一些可選屬性不需要的時候可以删除。
官方文檔:https://github.com/alibaba/Sentinel/wiki/在生産環境中使用-Sentinel
15、限流規則如何推送到Nacos進行持久化?
sentinel預設的持久化隻能從nacos推送到sentinel控制台,但是實際生産中肯定是雙向修改都能推送的,這個如何解決呢?
其實sentinel官方文檔就有說到解決方法,不過需要自己修改sentinel控制台的源碼來實作。
這個還是比較複雜的,sentinel隻幫我們實作了流控規則的demo,其他的還是要自己修改,這點不太人性化....
在這之前需要自己下載下傳對應版本的sentinel控制台的源碼,位址:https://github.com/alibaba/Sentinel/tags
流控規則源碼修改
在源碼的test目錄下有sentinel提供的demo,分别有apollo、nacos、zookeeper,如下圖:
這裡我們是Nacos,是以隻需要nacos包下面的demo。修改步驟如下:
1、去掉sentinel-datasource-nacos依賴的scop
這個sentinel-datasource-nacos依賴預設是
<scope>test</scope>
,是以我們需要去掉這個,如下:
<!-- for Nacos rule publisher sample --> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> </dependency>
如果你內建的zookeeper或者apollo,則把相應的依賴也要修改。
2、複制test環境下的nacos整個包到main下
将這個nacos包複制到
com.alibaba.csp.sentinel.dashboard.rule
這個包下,如下圖:
3、将FlowControllerV2中的代碼複制到FlowControllerV1中
com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2
這個是sentinel提供的demo,隻需要将其中的代碼全部覆寫到
com.alibaba.csp.sentinel.dashboard.controller.FlowControllerV1
中。
4、修改FlowControllerV1中的代碼
直接覆寫掉當然不行,還要做一些修改,如下:
- 修改RequestMapping中的請求url為
/v1/flow
- 修改
ruleProvider
的依賴,修改後的代碼如下:rulePublisher
@Autowired //使用nacos的依賴 @Qualifier("flowRuleNacosProvider") private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider; @Autowired //使用nacos的依賴 @Qualifier("flowRuleNacosPublisher") private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
5、注意nacos的相關配置
com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil
這個工具類中對應的是限流規則在nacos中的一些配置項,有
groupId
dataId
...對應的配置如下:
需要兩邊統一,可以自己修改。
com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfig
這個類中有個方法如下圖:
預設指定的nacos位址是本地的,這個需要修改。
6、完成
以上步驟已經改造了sentinel控制台的流控規則,打包啟動控制台代碼,指令如下:
mvn clean install -DskipTests=true -pl sentinel-dashboard -am
啟動後在控制台添加流控規則,可以看到也會同步推送到nacos,包括增删改。
其他規則修改也很簡單,照葫蘆畫瓢,這裡就不再詳細說了,後面會單獨出一篇文章詳細說一下。
16、叢集流控如何做?
首先一個簡單的問題:為什麼需要叢集流控?單機流控不香嗎?原因如下:
- 對于微服務要想保證高可用,必須是叢集,假設有100個叢集,那麼想要設定流控規則,是不是每個微服務都要設定一遍?維護成本太高了
- 單體流控還會造成流量不均勻的問題,出現總流控門檻值沒有達到某些微服務已經被限流了,這個是非常糟糕的問題,是以實際生産中對于叢集不推薦單體流控。
那麼如何解決上述的問題呢?sentinel為我們提供了叢集流控的規則。思想很簡單就是提供一個專門的server來統計調用的總量,其他的執行個體都與server保持通信。
叢集流控可以精确地控制整個叢集的調用總量,結合單機限流兜底,可以更好地發揮流量控制的效果。
叢集流控中共有兩種身份:
- Token Client:叢集流控用戶端,用于向所屬 Token Server 通信請求 token。叢集限流服務端會傳回給用戶端結果,決定是否限流。
- Token Server:即叢集流控服務端,處理來自 Token Client 的請求,根據配置的叢集規則判斷是否應該發放 token(是否允許通過)。
sentinel的叢集限流有兩種模式,分别如下:
- 獨立模式(Alone):即作為獨立的 token server 程序啟動,獨立部署,隔離性好,但是需要額外的部署操作。獨立模式适合作為 Global Rate Limiter 給叢集提供流控服務。
- 嵌入模式(Embedded):即作為内置的 token server 與服務在同一程序中啟動。在此模式下,叢集中各個執行個體都是對等的,token server 和 client 可以随時進行轉變,是以無需單獨部署,靈活性比較好。但是隔離性不佳,需要限制 token server 的總 QPS,防止影響應用本身。嵌入模式适合某個應用叢集内部的流控。
下面就以嵌入模式為例介紹一下如何配置。
就以
sentinel-openfeign-provider9009
這個子產品作為示範,直接啟動三個叢集,端口分别為
9009
9011
9013
,如下圖:
啟動成功,在sentinel控制台将會看到有三個執行個體已經被監控了,如下圖:
此時隻需要在控制台指定一個服務為token server,其他的為token client,叢集流控->新增token server,操作如下圖:
選取一個作為服務端,另外兩個作為用戶端,此時就已經配置好了,如下圖:
此時就可以添加叢集流控規則了,可以在sentinel控制台直接添加,也可以通過Nacos直接配置,下圖是通過Nacos配置的,如下圖:
Nacos推送成功後将會在sentinel控制台看到這條流控規則的配置,如下圖:
OK,至此叢集流控到這兒就介紹完了,配置好之後可以自己試一下效果,陳某就不再示範了。
官方文檔:https://github.com/alibaba/Sentinel/wiki/叢集流控
17、網關限流如何配置?
這一塊内容在後續介紹到網關的時候會詳細講,這裡就不再細說了,有想要了解的可以看官方文檔。
官方文檔:https://github.com/alibaba/Sentinel/wiki/網關限流
18、整合openFeign如何實作熔斷降級?
這個在上篇openFeign的文章中有詳細介紹:openFeign奪命連環9問,這誰受得了?陳某這裡就不再重複介紹了,有不知道的可以看上面這篇文章。
最後說一句
陳某碼字不易,這篇文章寫了兩周,如果覺得不錯,點贊、轉發、在看、收藏支援一下,謝謝!
以上源碼已經上傳GitHub,需要的公衆号【碼猿技術專欄】回複關鍵詞 9528
擷取。
本文來自部落格園,作者:不才陳某,轉載請注明原文連結:https://www.cnblogs.com/cbvlog/p/15385100.html
歡迎關注個人公衆号:碼猿技術專欄