1.1 Sentinel介紹
目錄
隨著微服務的流行,服務和服務之間的穩定性變得越來越重要。Sentinel 是面向分散式服務架構的流量控制組件,主要以流量為切入點,從限流、流量整形、熔斷降級、系統負載保護、熱點防護等多個維度來幫助開發者保障微服務的穩定性。
Pt1 基本概念
-
限流
限流是從系統能夠承受的請求量考慮,應對突發流量的是系統保護策略。當請求的流量異常(突發流量或者是流量過載)時,為了進行自我保護,超過的流量將直接被丟棄或者進行排隊處理(導致平均響應時間RT增高)。
-
熔斷
熔斷是從外部服務故障考慮,應對外部故障時的系統保護策略。主要有兩種場景:上游系統請求量異常,對上游請求流量進行熔斷,防止系統被壓垮;下游依賴成功率或處理時間異常,對下游進行熔斷,防止因下游導致自身處理變慢而導致的流量異常被壓垮。
-
降級
當自身服務發生故障時,可以關閉當前服務,約定一個快速失敗的Fallback,防止對上游呼叫鏈造成更大的影響。比如:連線超時、網路延遲、伺服器響應時間過長。
-
限流 Vs 熔斷 Vs 降級 區別
限流主要出於對異常流量考慮,為了保證系統不被突發流量擊垮。
熔斷是出於服務故障考慮,應對服務中不穩定因素,防止遠端服務故障引發的資源阻塞導致自身服務風險。
降級通常是和熔斷一起使用,犧牲非核心功能,或者對故障服務執行快速失敗處理,防止引發雪崩效應。
-
資源(需要被保護的)
資源是 Sentinel 的關鍵概念。它可以是 Java 應用程式中的任何內容,例如,由應用程式提供的服務,或由應用程式呼叫的其它應用提供的服務,甚至可以是一段程式碼。只要通過 Sentinel API 定義的程式碼,就是資源,能夠被 Sentinel 保護起來。大部分情況下,可以使用方法簽名,URL,甚至服務名稱作為資源名來標示資源。
-
規則(限流規則/熔斷規則)
圍繞資源的實時狀態設定的規則,可以包括流量控制規則、熔斷降級規則以及系統保護規則。所有規則可以動態實時調整。
Pt2 主要特性
Sentinel 分為兩個部分:
-
核心庫(Java 客戶端)不依賴任何框架/庫,能夠運行於所有 Java 執行時環境,同時對 Dubbo / Spring Cloud 等框架也有較好的支援。
-
控制檯(Dashboard)基於 Spring Boot 開發,打包後可以直接執行,不需要額外的 Tomcat 等應用容器。
Pt3 核心原理
Sentinel 的主要工作機制如下:
-
對主流框架提供適配或者顯示的 API,來定義需要保護的資源,並提供設施對資源進行實時統計和呼叫鏈路分析。
-
根據預設的規則,結合對資源的實時統計資訊,對流量進行控制。同時,Sentinel 提供開放的介面,方便您定義及改變規則。
-
Sentinel 提供實時的監控系統,方便您快速瞭解目前系統的狀態。
Pt3.1 Sentinel框架
在 Sentinel 裡面,所有的資源都對應一個資源名稱(resourceName
),每次資源呼叫都會建立一個 Entry
物件。Entry 可以通過對主流框架的適配自動建立,也可以通過註解的方式或呼叫 SphU
API 顯式建立。Entry 建立的時候,同時也會建立一系列功能插槽(slot chain),這些插槽有不同的職責,例如:
-
NodeSelectorSlot
負責收集資源的路徑,並將這些資源的呼叫路徑,以樹狀結構儲存起來,用於根據呼叫路徑來限流降級; -
ClusterBuilderSlot
則用於儲存資源的統計資訊以及呼叫者資訊,例如該資源的 RT, QPS, thread count 等等,這些資訊將用作為多維度限流,降級的依據; -
StatisticSlot
則用於記錄、統計不同緯度的 runtime 指標監控資訊; -
FlowSlot
則用於根據預設的限流規則以及前面 slot 統計的狀態,來進行流量控制; -
AuthoritySlot
則根據配置的黑白名單和呼叫來源資訊,來做黑白名單控制; -
DegradeSlot
則通過統計資訊以及預設的規則,來做熔斷降級; -
SystemSlot
則通過系統的狀態,例如 load1 等,來控制總的入口流量;
總體的框架如下:
Sentinel 將 ProcessorSlot
作為 SPI 介面進行擴充套件(1.7.2 版本以前 SlotChainBuilder
作為 SPI),使得 Slot Chain 具備了擴充套件的能力。您可以自行加入自定義的 slot 並編排 slot 間的順序,從而可以給 Sentinel 新增自定義的功能。
Pt3.2 常用Slot
NodeSelectorSlot
這個 slot 主要負責收集資源的路徑,並將這些資源的呼叫路徑以樹狀結構儲存起來,用於根據呼叫路徑進行流量控制。
ContextUtil.enter("entrance1", "appA"); Entry nodeA = SphU.entry("nodeA"); if (nodeA != null) { nodeA.exit(); } ContextUtil.exit();
上述程式碼通過 ContextUtil.enter()
建立了一個名為 entrance1
的上下文,同時指定呼叫發起者為 appA
;接著通過SphU.entry()
請求一個 token,如果該方法順利執行沒有拋 BlockException
,表明 token 請求成功。
以上程式碼將在記憶體中生成以下結構:
machine-root / / EntranceNode1 / / DefaultNode(nodeA)
注意:每個 DefaultNode
由資源 ID 和輸入名稱來標識。換句話說,一個資源 ID 可以有多個不同入口的 DefaultNode。
ContextUtil.enter("entrance1", "appA"); Entry nodeA = SphU.entry("nodeA"); if (nodeA != null) { nodeA.exit(); } ContextUtil.exit(); ContextUtil.enter("entrance2", "appA"); nodeA = SphU.entry("nodeA"); if (nodeA != null) { nodeA.exit(); } ContextUtil.exit();
以上程式碼將在記憶體中生成以下結構:
machine-root / \ / \ EntranceNode1 EntranceNode2 / \ / \ DefaultNode(nodeA) DefaultNode(nodeA)
上面的結構可以通過呼叫 curl http://localhost:8719/tree?type=root
來顯示:
EntranceNode: machine-root(t:0 pq:1 bq:0 tq:1 rt:0 prq:1 1mp:0 1mb:0 1mt:0) -EntranceNode1: Entrance1(t:0 pq:1 bq:0 tq:1 rt:0 prq:1 1mp:0 1mb:0 1mt:0) --nodeA(t:0 pq:1 bq:0 tq:1 rt:0 prq:1 1mp:0 1mb:0 1mt:0) -EntranceNode2: Entrance1(t:0 pq:1 bq:0 tq:1 rt:0 prq:1 1mp:0 1mb:0 1mt:0) --nodeA(t:0 pq:1 bq:0 tq:1 rt:0 prq:1 1mp:0 1mb:0 1mt:0) t:threadNum pq:passQps bq:blockedQps tq:totalQps rt:averageRt prq: passRequestQps 1mp:1m-passed 1mb:1m-blocked 1mt:1m-total
ClusterBuilderSlot
此插槽用於構建資源的 ClusterNode
以及呼叫來源節點。ClusterNode
保持某個資源執行統計資訊(響應時間、QPS、block 數目、執行緒數、異常數等)以及呼叫來源統計資訊列表。呼叫來源的名稱由 ContextUtil.enter(contextName,origin)
中的 origin
標記。可通過如下命令檢視某個資源不同調用者的訪問情況:curl http://localhost:8719/origin?id=caller
:
id: nodeA idx origin threadNum passedQps blockedQps totalQps aRt 1m-passed 1m-blocked 1m-total 1 caller1 0 0 0 0 0 0 0 0 2 caller2 0 0 0 0 0 0 0 0
StatisticSlot
StatisticSlot
是 Sentinel 的核心功能插槽之一,用於統計實時的呼叫資料。
-
clusterNode
:資源唯一標識的 ClusterNode 的實時統計 -
origin
:根據來自不同調用者的統計資訊 -
defaultnode
: 根據入口上下文區分的資源 ID 的 runtime 統計 -
入口流量的統計
Sentinel 底層採用高效能的滑動視窗資料結構 LeapArray
來統計實時的秒級指標資料,可以很好地支撐寫多於讀的高併發場景。
FlowSlot
這個 slot 主要根據預設的資源的統計資訊,按照固定的次序,依次生效。如果一個資源對應兩條或者多條流控規則,則會根據如下次序依次檢驗,直到全部通過或者有一個規則生效為止:
-
指定應用生效的規則,即針對呼叫方限流的;
-
呼叫方為 other 的規則;
-
呼叫方為 default 的規則。
DegradeSlot
這個 slot 主要針對資源的平均響應時間(RT)以及異常比率,來決定資源是否在接下來的時間被自動熔斷掉。
SystemSlot
這個 slot 會根據對於當前系統的整體情況,對入口資源的呼叫進行動態調配。其原理是讓入口的流量和當前系統的預計容量達到一個動態平衡。
注意系統規則只對入口流量起作用(呼叫型別為 EntryType.IN
),對出口流量無效。可通過 SphU.entry(res, entryType)
指定呼叫型別,如果不指定,預設是EntryType.OUT
。
Pt3.3 核心類
ProcessorSlotChain
Sentinel 的核心骨架,將不同的 Slot 按照順序串在一起(責任鏈模式),從而將不同的功能(限流、降級、系統保護)組合在一起。slot chain 其實可以分為兩部分:統計資料構建部分(statistic)和判斷部分(rule checking)。
核心結構:
目前的設計是 one slot chain per resource,因為某些 slot 是 per resource 的(比如 NodeSelectorSlot)。
Context
Context 代表呼叫鏈路上下文,貫穿一次呼叫鏈路中的所有 Entry
。Context 維持著入口節點(entranceNode
)、本次呼叫鏈路的 curNode、呼叫來源(origin
)等資訊。Context 名稱即為呼叫鏈路入口名稱。
Context 維持的方式:通過 ThreadLocal 傳遞,只有在入口 enter
的時候生效。由於 Context 是通過 ThreadLocal 傳遞的,因此對於非同步呼叫鏈路,執行緒切換的時候會丟掉 Context,因此需要手動通過 ContextUtil.runOnContext(context, f)
來變換 context。
Entry
每一次資源呼叫都會建立一個 Entry
。Entry
包含了資源名、curNode(當前統計節點)、originNode(來源統計節點)等資訊。
CtEntry
為普通的 Entry
,在呼叫 SphU.entry(xxx)
的時候建立。特性:Linked entry within current context(內部維護著 parent
和 child
)
需要注意的一點:CtEntry 建構函式中會做呼叫鏈的變換,即將當前 Entry 接到傳入 Context 的呼叫鏈路上(setUpEntryFor
)。
資源呼叫結束時需要 entry.exit()
。exit 操作會過一遍 slot chain exit,恢復呼叫棧,exit context 然後清空 entry 中的 context 防止重複呼叫。
Node
Sentinel 裡面的各種種類的統計節點:
-
StatisticNode
:最為基礎的統計節點,包含秒級和分鐘級兩個滑動視窗結構。 -
DefaultNode
:鏈路節點,用於統計呼叫鏈路上某個資源的資料,維持樹狀結構。 -
ClusterNode
:簇點,用於統計每個資源全域性的資料(不區分呼叫鏈路),以及存放該資源的按來源區分的呼叫資料(型別為StatisticNode
)。特別地,Constants.ENTRY_NODE
節點用於統計全域性的入口資源資料。 -
EntranceNode
:入口節點,特殊的鏈路節點,對應某個 Context 入口的所有呼叫資料。Constants.ROOT
節點也是入口節點。
構建的時機:
-
EntranceNode
在ContextUtil.enter(xxx)
的時候就建立了,然後塞到 Context 裡面。 -
NodeSelectorSlot
:根據 context 建立DefaultNode
,然後 set curNode to context。 -
ClusterBuilderSlot
:首先根據 resourceName 建立ClusterNode
,並且 set clusterNode to defaultNode;然後再根據 origin 建立來源節點(型別為StatisticNode
),並且 set originNode to curEntry。
幾種 Node 的維度(數目):
-
ClusterNode
的維度是 resource -
DefaultNode
的維度是 resource * context,存在每個 NodeSelectorSlot 的map
裡面 -
EntranceNode
的維度是 context,存在 ContextUtil 類的contextNameNodeMap
裡面 -
來源節點(型別為
StatisticNode
)的維度是 resource * origin,存在每個 ClusterNode 的originCountMap
裡面
StatisticSlot
StatisticSlot
是 Sentinel 最為重要的類之一,用於根據規則判斷結果進行相應的統計操作。
entry 的時候:依次執行後面的判斷 slot。每個 slot 觸發流控的話會丟擲異常(BlockException
的子類)。若有 BlockException
丟擲,則記錄 block 資料;若無異常丟擲則算作可通過(pass),記錄 pass 資料。
exit 的時候:若無 error(無論是業務異常還是流控異常),記錄 complete(success)以及 RT,執行緒數-1。
記錄資料的維度:執行緒數+1、記錄當前 DefaultNode 資料、記錄對應的 originNode 資料(若存在 origin)、累計 IN 統計資料(若流量型別為 IN)。