簡介
Sentinel提供了豐富的限流、熔斷功能,它支持控制臺配置限流、熔斷規則,支持集群限流,并可以將相應服務呼叫情況可視化,
目前已經有很多專案接入了Sentinel,而本文主要是對Sentinel的限流功能做一次詳細的分析,
Sentinel會進行流量統計,執行流量控制規則,而統計資料的展示和規則的設定在 sentinel-dashboard 專案中,這是一個 Spring MVC 應用,有后臺管理界面,我們通過這個管理后臺和各個應用進行互動,
當然,你不一定需要 dashboard,僅僅使用 sentinel-core,它會將統計資訊寫入到指定的日志檔案中,通過該檔案內容來了解每個介面的流量情況,這時只是使用到了 Sentinel 的流量監控功能,
dashboard 應用默認不持久化資料,它的所有資料都在記憶體,所以 dashboard 重啟意味著所有資料都會丟失,你應按需要定制化 dashboard,如至少你應該要持久化規則設定,QPS 資料非常適合存放在時序資料庫中,當然如果你的資料量不大,存 MySQL 也問題不大,定期清理一下過期資料即可,因為大部分人應該不會關心一個月以前的 QPS,
- Sentinel 的核心將不同 Slot 按序串在一起(責任鏈模式),從而將不同功能(限流、降級、系統保護)組合在一起,核心結構:

slot chain 其實可以分為兩部分:統計資料構建部分(statistic)和判斷部分(rule checking),
Sentinel 的資料統計
資料統計模塊的內容,這樣讀者在后面看到相應的內容的時候心里有一些底,這節內容還是比較簡單的,當然,如果你希望立馬進入 Sentinel 的主流程,可以先跳過這一節,
Sentinel 的定位是流量控制,它有兩個維度的控制,一個是控制并發執行緒數,另一個是控制 QPS,它們都是針對某個具體的介面來設定的,其實順澩比較準確,Sentinel 把控制的粒度定義為 Resource,
要做控制,首先就要先做統計,它要知道當前介面的 QPS 和并發是多少,進而判斷一個新的請求能不能讓它通過,
資料統計的代碼在
StatisticNode

QPS 資料使用了滑動視窗:

- 保存最近60秒的統計資訊, windowLengthInMs 故意設定為1000毫秒,表示每秒每個桶,這樣我們就可以獲得每秒的準確統計資訊,

- 執行緒數量的計數器,即統計并發量

可知,Sentinel 統計了 秒 和 分 兩個維度,現在看其實作類
ArrayMetric
Sentinel中的基本度量標準類,使用內部 BucketLeapArray,

屬性
- 以分鐘維度統計的使用來說,使用子類 BucketLeapArray 實作,

構造器

LeapArray
欄位

-
條件(謂詞)更新鎖,僅在不使用當前桶時使用,

-
內部核心陣列 array,它的長度為 60,就是有 60 個視窗,每個視窗長度為 1 秒,一分鐘走完一輪,
然后下一輪開啟“覆寫”操作,

每個視窗是一個 WindowWrap 類實體,
添加資料的時候,先判斷當前走到哪個視窗了(當前時間(s) % 60 即可),然后需要判斷這個視窗是否是過期資料,如果是過期資料(視窗代表的時間距離當前已經超過 1 分鐘),需要先重置這個視窗實體的資料,
統計資料同理,如統計過去一分鐘的 QPS 資料,就是將每個視窗的值相加,當中需要判斷視窗資料是否是過期資料,即判斷視窗的 WindowWrap 實體是否是一分鐘內的資料,
核心邏輯都封裝在了 currentWindow(long timeMillis) 和 values(long timeMillis)方法中,
添加資料的時候,我們要先獲取操作的目標視窗,也就是
分維度資料統計
currentWindow
Sentinel 在這里處理初始化和過期重置的情況

獲取資料,使用的是
values
- 回傳“有效”視窗中的資料

- isWindowDeprecated

案例

紅色部分的Context 代表一個呼叫鏈的入口,Context 實體設定在 ThreadLocal,所以它是跟著執行緒走的,如果要切換執行緒,需要手動切換,
ContextUtil#enter 有倆引數:
-
context name
呼叫鏈的入口,以區分不同呼叫鏈路,默認是
-
origin
呼叫方標識,作用- 黑白名單的授權控制
- 統計諸如從應用 application-a 發起的對當前應用 interfaceXxx() 介面的呼叫,目前這個資料會被統計,但是 dashboard 中并不展示
進入 BlockException 例外分支,代表該次請求被流量控制規則限制,一般會讓代碼走入到熔斷降級邏輯,當然,BlockException 其實有好多個子類

亦可 catch 具體子類處理,
SphU#entry 方法的引數:
-
第一個引數
標識資源,通常就是我們的介面標識,對于資料統計、規則控制等,我們一般都是在這個粒度上進行的,根據這個字串來唯一標識,它會被包裝成 ResourceWrapper 實體, -
第二個引數
標識資源的型別

- EntryType.IN
入口流量,比如我們的介面對外提供服務,那通常就是控制入口流量 - EntryType.OUT

默認就是出口流量,它的業務需要呼叫訂單服務,像這種情況,壓力其實都在訂單服務,那就指定它為出口流量,
- EntryType.IN
流量型別在 SystemSlot 類中用以實作自適應限流,根據系統健康狀態來判斷是否要限流,如果是 OUT 型別,由于壓力在外部系統中,所以就不需要執行該規則,
若在一個方法中寫,要注意內層的 Entry 先 exit,才能做外層的 exit,否則會拋出例外,原始碼角度來看,是在 Context 實體中,保存了當前的 Entry 實體,
原始碼決議
ContextUtil
static 代碼塊
這里會添加一個默認的 EntranceNode 實體,

enter
該行代碼可不寫,通常情況下,都不會顯示設定 context,
ContextUtil.enter("user-center", "app-A");
如果不顯式呼叫該方法,就會進入到默認 context,

然后上面的這個方法會走進 ContextUtil#trueEnter,添加名為 “user-center” 的 EntranceNode 節點:

若不顯式呼叫 ContextUtil#enter,那 root 就只有一個默認節點 sentinel_default_context,
context,執行緒執行的背景關系,在 Sentinel 中對于一個新的 context name,Sentinel 會往樹中添加一個 EntranceNode 實體,所以它的作用是為了區分呼叫鏈路,標識呼叫入口,在 sentinel-dashboard 中,我們可以很直觀地看出呼叫鏈路:

SphU
entry
CtSph#entryWithPriority

lookProcessChain(resourceWrapper)
鏈中每一個節點是一個 Slot 實體,這個鏈通過 BlockException 例外來告知呼叫入口最終的執行情況,
Sentinel 提供了 SPI 端點,讓我們可以自己定制 Builder,如添加一個 Slot 進去,
由于 SlotChainBuilder 介面設計,我們只能全域所有的 resource 使用相同的責任鏈配置,

按照默認的 DefaultSlotChainBuilder 生成的責任鏈繼續原始碼,
對相同的 resource,使用同一責任鏈實體,不同 resource,使用不同責任鏈實體,
resource 實體根據 resource name 來判斷,和執行緒沒有關系,
參考
- https://juejin.cn/post/6906302891875647495
- https://github.com/alibaba/Sentinel/wiki/Sentinel-%E6%A0%B8%E5%BF%83%E7%B1%BB%E8%A7%A3%E6%9E%90
- https://www.javadoop.com/post/sentinel
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/237187.html
標籤:AI
