Sentinel是阿里開源的一款高性能的限流框架。這里將對Sentinel的使用和實現進行介紹。
這里先介紹下Sentinel中涉及到的基本概念,包括使用上或者實現上。主要是筆者在閱讀文檔和源碼時經常會接觸到的對象。
Resource
資源是整個Sentinel最基本的一個概念。可以是一段代碼,一個http請求,一個微服務,總而言之,他是Sentinel需要保證的實體。大部分情況下,我們可以使用方法簽名,URL或者是服務名稱來作為資源的名稱。它在Sentinel中的體現是:ResourceWrapper,他有兩個子類:
-
StringResourceWrapper 使用string來標識一個資源
-
MethodResouceWrapper 使用一個函數簽名來標識一個資源
Node
節點是用來存儲統計數據的基本數據單元,Node本身只是一個接口,它有多個實現:
-
StatisticNode 唯一的直接實現類,實現了流量統計的基本方法,在StatisticSlot中使用
-
ClusterNode 繼承自StatisticNode,對於某一個資源的全局統計
-
DefaultNode 繼承自StatisticNode,對於某一個資源在相應上下文中的實現,保存了一個指向ClusterNode的引用。另外還保存了子節點列表,當在同一個context下多次調用SphU.entry不同資源時會創建子節點
-
EntranceNode 繼承自DefaultNode,代表一個調用的根節點,一個Context會對應到一個EntranceNode
Context
上下文是用來保存當前調用的元數據,存儲在ThreadLocal中,它包含了幾個信息:
-
EntranceNode 整個調用樹的根節點,即入口
-
Entry 當前的調用點
-
Node 關聯到當前調用點的統計信息
-
Origin 通常用來標識調用方,這在我們需要按照調用方來區分流控策略的時候會非常有用
每當我們調用SphU.entry()或者 SphO.entry()獲取訪問資源許可的時候都需要當前線程處在某個context中,如果我們沒有顯式調用ContextUtil.enter(),默認會使用Default context。如果我們在一個上下文中多次調用SphU.entry()來獲取多個資源,一個調用樹就會被創建出來
NullContext
超過系統能夠創建的最大會話數則返回NullContext,后續不對該會話做過濾校驗,直接放過。
Entry
每次SphU.entry()調用都會返回一個Entry,Entry保持了所有關於當前資源調用的信息:
-
createTime 這個資源調用的創建時間
-
currentNode SphU.entry請求進入的資源在當前上下文中的統計數據Node
-
originNode SphU.entry請求進入的資源對於特定origin調用方的統計數據node
Entry的實現類為CtEntry,它其中除了上述信息之外,還保存了額外的信息:
-
parent 調用樹鏈條中上一個entry
-
child 調用樹鏈條中的下一個entry
-
chain 當前調用資源所使用的限流工作責任鏈,包括各個Slot
-
context 當前調用點所屬的上下文
EntryType
EntryType 說的是這次請求的流量類型,共有兩種類型:IN 和 OUT 。
IN:是指進入我們系統的入口流量,比如 http 請求或者是其他的 rpc 之類的請求。
OUT:是指我們系統調用其他第三方服務的出口流量。
入口、出口流量只有在配置了系統規則時才有效。
設置Type 為 IN 是為了統計整個系統的流量水平,防止系統被打垮,用以自我保護的一種方式。
設置Type 為 OUT 一方面是為了保護第三方系統,比如我們系統依賴了一個生成訂單號的接口,而這個接口是核心服務,如果我們的服務是非核心應用的話需要對他進行限流保護;另一方面也可以保護自己的系統,假設我們的服務是核心應用,而依賴的第三方應用老是超時,那這時可以通過設置依賴的服務的 rt 來進行降級,這樣就不至於讓第三方服務把我們的系統拖垮。
Slot
Entry 創建的時候,同時也會創建一系列功能插槽(slot chain),這些插槽有不同的職責,例如:
-
NodeSelectorSlot 負責收集資源的路徑,並將這些資源的調用路徑,以樹狀結構存儲起來,用於根據調用路徑來限流降級;
-
ClusterBuilderSlot 則用於存儲資源的統計信息以及調用者信息,例如該資源的 RT, QPS,thread count 等等,這些信息將用作為多維度限流,降級的依據;
-
LogSlot 用於打印日志
-
StatisticSlot 則用於記錄、統計不同緯度的 runtime 指標監控信息;
-
SystemSlot 則通過系統的狀態,例如 load1 等,來控制總的入口流量;
-
AuthoritySlot 則根據配置的黑白名單和調用來源信息,來做黑白名單控制;
-
FlowSlot 則用於根據預設的限流規則以及前面 slot 統計的狀態,來進行流量控制;
-
DegradeSlot 則通過統計信息以及預設的規則,來做熔斷降級;
Slot只綁定在CtEntry上
ProcessorSlotChain
功能槽處理鏈,entry進入一個槽可以添加自己的動作,之后后fire動作會entry下一個槽,exit同理
注意,實現上相同資源共享一個ProcessorSlotChain ,可以跨Context
LimitApp
流控規則中的 limitApp 字段用於根據調用來源進行流量控制。該字段的值有以下三種選項,分別對應不同的場景:
-
default:表示不區分調用者,來自任何調用者的請求都將進行限流統計。如果這個資源名的調用總和超過了這條規則定義的閾值,則觸發限流。
-
{some_origin_name}:表示針對特定的調用者,只有來自這個調用者的請求才會進行流量控制。例如 NodeA 配置了一條針對調用者caller1的規則,那么當且僅當來自 caller1 對 NodeA 的請求才會觸發流量控制。
-
other:表示針對除 {some_origin_name} 以外的其余調用方的流量進行流量控制。例如,資源NodeA配置了一條針對調用者 caller1 的限流規則,同時又配置了一條調用者為 other 的規則,那么任意來自非 caller1 對 NodeA 的調用,都不能超過 other 這條規則定義的閾值。
同一個資源名可以配置多條規則,規則的生效順序為:{some_origin_name} > other > default
介紹完了上面的基本概念,下面給出Sentinel的基本用法:
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule1 = new FlowRule();
rule1.setResource(KEY);
// set limit qps to 20
rule1.setCount(20);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
Entry entry = null;
try {
entry = SphU.entry(KEY);
// token acquired, means pass,do biz logic
} catch (BlockException e1) {
//block,handle block logic
} catch (Exception e2) {
// biz exception,handle biz exception logic
} finally {
if (entry != null) {
entry.exit();
}
}
如上,為sentinel的基本用法:
先設定好規則,在進入需要受保護的資源前,嘗試獲取token,若成功獲取token,則可以執行相關邏輯,否則拋出異常進行處理,最后釋放所獲得的token 。
個人公眾號:啊駝