Sentinel限流
- 流控規則
-
- 基本介紹
- 流控模式
- 流控效果
- 代碼示範
-
- 流控模式
-
- 直接(預設)
- 關聯
- 鍊路
- 流控效果
- 直接
- 預熱
- 排隊等待
- 降級規則
-
- 名詞介紹
-
- RT(平均響應時間,秒級)
- 異常比例(秒級)
- 異常數(分鐘級)
- 概念
- 降級政策實戰
-
- RT
- 異常比例
- 異常數
- Sentinel熱點規則
-
- 什麼是熱點資料
- 兜底的方法
- 配置
- 參數例外項
- 結語
- Sentinel系統配置
- @SentinelResource注解
-
- 問題
- 客戶自定義限流處理邏輯
- 更多注解屬性說明
流控規則
基本介紹
字段說明
資源名:唯一名稱,預設請求路徑
針對來源:Sentinel可以針對調用者進行限流,填寫微服務名,預設default(不區分來源)
門檻值類型 / 單機門檻值
QPS:(每秒鐘的請求數量):但調用該API的QPS達到門檻值的時候,進行限流
線程數:當調用該API的線程數達到門檻值的時候,進行限流
是否叢集:不需要叢集
流控模式
- 直接:api都達到限流條件時,直接限流
- 關聯:當關聯的資源達到門檻值,就限流自己
- 鍊路:隻記錄指定鍊路上的流量(指定資源從入口資源進來的流量,如果達到門檻值,就進行限流)【API級别的針對來源】
流控效果
- 快速失敗:直接失敗,抛異常
- Warm UP:根據codeFactory(冷加載因子,預設3),從門檻值/CodeFactor,經過預熱時長,才達到設定的QPS門檻值
- 排隊等待:勻速排隊,讓請求以勻速的速度通過,門檻值類型必須設定QPS,否則無效
代碼示範
流控模式
直接(預設)
我們給testA增加流控
然後我們請求
http://localhost:8401/testA
,就會出現失敗,被限流,快速失敗
思考:
直接調用的是預設報錯資訊,能否有我們的後續處理,比如更加友好的提示,類似有hystrix的fallback方法
線程數
這裡的線程數表示一次隻有一個線程進行業務請求,目前出現請求無法響應的時候,會直接報錯,例如,在方法的内部增加一個睡眠,那麼後面來的就會失敗
@GetMapping("/testD")
public String testD()
{
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
return "------testD";
}
關聯
當關聯的資源達到門檻值時,就限流自己
當與A關聯的資源B達到門檻值後,就限流A自己,B惹事,A挂了
場景:支付接口達到門檻值後,就限流下訂單的接口
設定:
當關聯資源 /testB的QPS達到門檻值超過1時,就限流/testA的Rest通路位址,當關聯資源達到門檻值後,限制配置好的資源名
這個使用我們利用postman模拟并發密集通路
testB
首先我們需要使用postman,建立一個請求
同時将請求儲存在 Collection中
然後點選箭頭,選中接口,選擇run
點選運作,大批量線程高并發通路B,導緻A失效了,同時我們點選通路
http://localhost:8401/testA
,結果發現,我們的A已經挂了
在測試A接口
這就是我們的關聯限流
鍊路
多個請求調用了同一個微服務
流控效果
直接
快速失敗,預設的流控處理
直接失敗,抛出異常:Blocked by Sentinel(Flow limiting)
預熱
系統最怕的就是出現,平時通路是0,然後突然一瞬間來了10W的QPS
公式:門檻值 除以 clodFactor(預設值為3),經過預熱時長後,才會達到門檻值
Warm Up方式,即預熱/冷啟動方式,當系統長期處于低水位的情況下,當流量突然增加時,直接把系統拉升到高水位可能會瞬間把系統壓垮。通過冷啟動,讓通過的流量緩慢增加,在一定時間内逐漸增加到門檻值,給冷系統一個預熱的時間,避免冷系統被壓垮。通常冷啟動的過程系統允許的QPS曲線如下圖所示
預設clodFactor為3,即請求QPS從threshold / 3開始,經預熱時長逐漸提升至設定的QPS門檻值
假設這個系統的QPS是10,那麼最開始系統能夠接受的 QPS = 10 / 3 = 3,然後從3逐漸在5秒内提升到10
應用場景:
秒殺系統在開啟的瞬間,會有很多流量上來,很可能把系統打死,預熱的方式就是為了保護系統,可能慢慢的把流量放進來,慢慢的把門檻值增長到設定的門檻值。
排隊等待
大家均速排隊,讓請求以均勻的速度通過,門檻值類型必須設定成QPS,否則無效
均速排隊方式必須嚴格控制請求通過的間隔時間,也即讓請求以勻速的速度通過,對應的是漏桶算法。
這種方式主要用于處理間隔性突發的流量,例如消息隊列,想象一下這樣的場景,在某一秒有大量的請求到來,而接下來的幾秒處于空閑狀态,我們系統系統能夠接下來的空閑期間逐漸處理這些請求,而不是在第一秒直接拒絕多餘的請求。
設定含義:/testA 每秒1次請求,超過的話,就排隊等待,等待時間超過20000毫秒
降級規則
名詞介紹
RT(平均響應時間,秒級)
- 平均響應時間,超過門檻值 且 時間視窗内通過的請求 >= 5,兩個條件同時滿足後出發降級
- 視窗期過後,關閉斷路器
- RT最大4900(更大的需要通過 -Dcsp.sentinel.staticstic.max.rt=XXXXX才能生效)
異常比例(秒級)
QPA >= 5 且異常比例(秒級)超過門檻值時,觸發降級;時間視窗結束後,關閉降級
異常數(分鐘級)
異常數(分鐘統計)超過門檻值時,觸發降級,時間視窗結束後,關閉降級
概念
Sentinel熔斷降級會在調用鍊路中某個資源出現不穩定狀态時(例如調用逾時或異常異常比例升高),對這個資源的調用進行限制,讓請求快速失敗,避免影響到其它的資源而導緻級聯錯誤。
當資源被降級後,在接下來的降級時間視窗之内,對該資源的調用都進行自動熔斷(預設行為是抛出DegradeException)
Sentinel的斷路器是沒有半開狀态
半開的狀态,系統自動去檢測是否請求有異常,沒有異常就關閉斷路器恢複使用,有異常則繼續打開斷路器不可用,具體可以參考hystrix
降級政策實戰
RT
平均響應時間 (
DEGRADE_GRADE_RT
):當 1s 内持續進入 N 個請求,對應時刻的平均響應時間(秒級)均超過門檻值(
count
,以 ms 為機關),那麼在接下的時間視窗(
DegradeRule
中的
timeWindow
,以 s 為機關)之内,對這個方法的調用都會自動地熔斷(抛出
DegradeException
)。注意 Sentinel 預設統計的 RT 上限是 4900 ms,超出此門檻值的都會算作 4900 ms,若需要變更此上限可以通過啟動配置項
-Dcsp.sentinel.statistic.max.rt=xxx
來配置。
代碼測試
@GetMapping("/testD")
public String testD()
{
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
log.info("testD 異常比例");
return "------testD";
}
然後使用Jmeter壓力測試工具進行測試
按照上述操作,永遠1秒種打進來10個線程,大于5個了,調用tesetD,我們希望200毫秒内處理完本次任務,如果200毫秒沒有處理完,在未來的1秒的時間視窗内,斷路器打開(保險絲跳閘)微服務不可用,保險絲跳閘斷電
Blocked by Sentinel (flow limiting)
後續我們停止使用jmeter,沒有那麼大的通路量了,斷路器關閉(保險絲恢複),微服務恢複OK
異常比例
異常比例 (
DEGRADE_GRADE_EXCEPTION_RATIO
):當資源的每秒請求量 >= N(可配置),并且每秒異常總數占通過量的比值超過門檻值(
DegradeRule
中的
count
)之後,資源進入降級狀态,即在接下的時間視窗(
DegradeRule
中的
timeWindow
,以 s 為機關)之内,對這個方法的調用都會自動地傳回。異常比率的門檻值範圍是
[0.0, 1.0]
,代表 0% - 100%。
單獨通路一次,必然來一次報錯一次,開啟jmeter後,直接高并發發送請求,多次調用達到我們的配置條件了,斷路器開啟(保險絲跳閘),微服務不可用,不在報錯,而是服務降級了
設定3秒内,如果請求百分50出錯,那麼就會熔斷
我們用jmeter每秒發送10次請求,3秒後,再次調用
localhost:8401/testD
出現服務降級
異常數
異常數 (
DEGRADE_GRADE_EXCEPTION_COUNT
):當資源近 1 分鐘的異常數目超過門檻值之後會進行熔斷。注意由于統計時間視窗是分鐘級别的,若
timeWindow
小于 60s,則結束熔斷狀态後仍可能再進入熔斷狀态
時間視窗一定要大于等于60秒
異常數是按分鐘來統計的
下面設定是,一分鐘内出現5次,則熔斷
首先我們再次通路
http://localhost:8401/testE
,第一次通路絕對報錯,因為除數不能為0,我們看到error視窗,但是達到5次報錯後,進入熔斷後的降級
Sentinel熱點規則
什麼是熱點資料
Github文檔傳送門
何為熱點?熱點即經常通路的資料。很多時候我們希望統計某個熱點資料中通路頻次最高的 Top K 資料,并對其通路進行限制。比如:
商品 ID 為參數,統計一段時間内最常購買的商品 ID 并進行限制
使用者 ID 為參數,針對一段時間内頻繁通路的使用者 ID 進行限制
熱點參數限流會統計傳入參數中的熱點參數,并根據配置的限流門檻值與模式,對包含熱點參數的資源調用進行限流。熱點參數限流可以看做是一種特殊的流量控制,僅對包含熱點參數的資源調用生效。
Sentinel 利用 LRU 政策統計最近最常通路的熱點參數,結合令牌桶算法來進行參數級别的流控。熱點參數限流支援叢集模式。
兜底的方法
分為系統預設的和客戶自定義的,兩種,之前的case中,限流出現問題了,都用sentinel系統預設的提示:Blocked By Sentinel,我們能不能自定義,類似于hystrix,某個方法出現問題了,就找到對應的兜底降級方法。
從
@HystrixCommand
到
@SentinelResource
配置
@SentinelResource的value,就是我們的資源名,也就是對哪個方法配置熱點規則
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2)
{
//int age = 10/0;
return "------testHotKey";
}
// 和上面的參數一樣,不錯需要加入 BlockException
public String deal_testHotKey (String p1, String p2, BlockException exception)
{
return "------deal_testHotKey,o(╥﹏╥)o"; // 兜底的方法
}
我們對參數0,設定熱點key進行限流
配置完成後
當我們不斷的請求時候,也就是以第一個參數為目标,請求接口,我們會發現多次請求後
http://localhost:8401/testHotKey?p1=a
就會出現以下的兜底錯誤
------deal_testHotKey,o(╥﹏╥)o
這是因為我們針對第一個參數進行了限制,當我們QPS超過1的時候,就會觸發兜底的錯誤
假設我們請求的接口是:
http://localhost:8401/testHotKey?p2=a
,我們會發現他就沒有進行限流
參數例外項
上述案例示範了第一個參數p1,當QPS超過1秒1次點選,馬上被限流
- 普通:超過一秒1個後,達到門檻值1後馬上被限流
- 我們期望p1參數當它達到某個特殊值時,它的限流值和平時不一樣
- 特例:假設當p1的值等于5時,它的門檻值可以達到200
- 一句話說:當key為特殊值的時候,不被限制
平時的時候,參數1的QPS是1,超過的時候被限流,但是有特殊值,比如5,那麼它的門檻值就是200
我們通過
http://localhost:8401/testHotKey?p1=5
一直重新整理,發現不會觸發兜底的方法,這就是參數例外項
熱點參數的注意點,參數必須是基本類型或者String
結語
@SentinelResource
處理的是Sentinel控制台配置的違規情況,有blockHandler方法配置的兜底處理
RuntimeException,如 int a = 10/0 ; 這個是java運作時抛出的異常,RuntimeException,@RentinelResource不管
也就是說:
@SentinelResource
主管配置出錯,運作出錯不管。
如果想要有配置出錯,和運作出錯的話,那麼可以設定 fallback
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey", fallback = "fallBack")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2)
{
//int age = 10/0;
return "------testHotKey";
}
Sentinel系統配置
Sentinel 系統自适應限流從整體次元對應用入口流量進行控制,結合應用的 Load、CPU 使用率、總體平均 RT、入口 QPS 和并發線程數等幾個次元的監控名額,通過自适應的流控政策,讓系統的入口流量和系統的負載達到一個平衡,讓系統盡可能跑在最大吞吐量的同時保證系統整體的穩定性。
系統保護規則是從應用級别的入口流量進行控制,從單台機器的 load、CPU 使用率、平均 RT、入口 QPS 和并發線程數等幾個次元監控應用名額,讓系統盡可能跑在最大吞吐量的同時保證系統整體的穩定性。
系統保護規則是應用整體次元的,而不是資源次元的,并且僅對入口流量生效。入口流量指的是進入應用的流量(
EntryType.IN
),比如 Web 服務或 Dubbo 服務端接收的請求,都屬于入口流量。
系統規則支援以下的模式:
- 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 達到門檻值即觸發系統保護。
這樣相當于設定了全局的QPS過濾
@SentinelResource注解
按資源名稱限流 + 後續處理
按URL位址限流 + 後續處理
問題
- 系統預設的,沒有展現我們自己的業務要求
- 依照現有條件,我們自定義的處理方法又和業務代碼耦合在一塊,不直覺
- 每個業務方法都添加一個兜底方法,那代碼膨脹加劇
- 全局統一的處理方法沒有展現
- 關閉8401,發現流控規則已經消失,說明這個是沒有持久化
客戶自定義限流處理邏輯
建立CustomerBlockHandler類用于自定義限流處理邏輯
public class CustomerBlockHandler
{
public static CommonResult handlerException(BlockException exception)
{
return new CommonResult(4444,"按客戶自定義,global handlerException----1");
}
public static CommonResult handlerException2(BlockException exception)
{
return new CommonResult(4444,"按客戶自定義,global handlerException----2");
}
}
那麼我們在使用的時候,就可以首先指定是哪個類,哪個方法
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class,
blockHandler = "handlerException2")
public CommonResult customerBlockHandler()
{
return new CommonResult(200,"按客戶自定義",new Payment(2020L,"serial003"));
}
更多注解屬性說明
所有的代碼都要用try - catch - finally 進行處理
sentinel主要有三個核心API
- Sphu定義資源
- Tracer定義統計
- ContextUtil定義了上下文