Sentinel是阿里開源的一款高性能的限流框架。這裏將對Sentinel的使用和實現進行介紹。node
這裏先介紹下Sentinel中涉及到的基本概念,包括使用上或者實現上。主要是筆者在閱讀文檔和源碼時常常會接觸到的對象。app
資源是整個Sentinel最基本的一個概念。能夠是一段代碼,一個http請求,一個微服務,總而言之,他是Sentinel須要保證的實體。大部分狀況下,咱們可使用方法簽名,URL或者是服務名稱來做爲資源的名稱。它在Sentinel中的體現是:ResourceWrapper,他有兩個子類:框架
StringResourceWrapper 使用string來標識一個資源函數
MethodResouceWrapper 使用一個函數簽名來標識一個資源微服務
節點是用來存儲統計數據的基本數據單元,Node自己只是一個接口,它有多個實現:性能
StatisticNode 惟一的直接實現類,實現了流量統計的基本方法,在StatisticSlot中使用ui
ClusterNode 繼承自StatisticNode,對於某一個資源的全局統計線程
DefaultNode 繼承自StatisticNode,對於某一個資源在相應上下文中的實現,保存了一個指向ClusterNode的引用。另外還保存了子節點列表,當在同一個context下屢次調用SphU.entry不一樣資源時會建立子節點日誌
EntranceNode 繼承自DefaultNode,表明一個調用的根節點,一個Context會對應到一個EntranceNodecode
上下文是用來保存當前調用的元數據,存儲在ThreadLocal中,它包含了幾個信息:
EntranceNode 整個調用樹的根節點,即入口
Entry 當前的調用點
Node 關聯到當前調用點的統計信息
Origin 一般用來標識調用方,這在咱們須要按照調用方來區分流控策略的時候會很是有用
每當咱們調用SphU.entry()或者 SphO.entry()獲取訪問資源許可的時候都須要當前線程處在某個context中,若是咱們沒有顯式調用ContextUtil.enter(),默認會使用Default context。若是咱們在一個上下文中屢次調用SphU.entry()來獲取多個資源,一個調用樹就會被建立出來
超過系統可以建立的最大會話數則返回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 說的是此次請求的流量類型,共有兩種類型:IN 和 OUT 。
IN:是指進入咱們系統的入口流量,好比 http 請求或者是其餘的 rpc 之類的請求。
OUT:是指咱們系統調用其餘第三方服務的出口流量。
入口、出口流量只有在配置了系統規則時纔有效。
設置Type 爲 IN 是爲了統計整個系統的流量水平,防止系統被打垮,用以自我保護的一種方式。
設置Type 爲 OUT 一方面是爲了保護第三方系統,好比咱們系統依賴了一個生成訂單號的接口,而這個接口是核心服務,若是咱們的服務是非核心應用的話須要對他進行限流保護;另外一方面也能夠保護本身的系統,假設咱們的服務是核心應用,而依賴的第三方應用總是超時,那這時能夠經過設置依賴的服務的 rt 來進行降級,這樣就不至於讓第三方服務把咱們的系統拖垮。
Entry 建立的時候,同時也會建立一系列功能插槽(slot chain),這些插槽有不一樣的職責,例如:
NodeSelectorSlot 負責收集資源的路徑,並將這些資源的調用路徑,以樹狀結構存儲起來,用於根據調用路徑來限流降級;
ClusterBuilderSlot 則用於存儲資源的統計信息以及調用者信息,例如該資源的 RT, QPS,thread count 等等,這些信息將用做爲多維度限流,降級的依據;
LogSlot 用於打印日誌
StatisticSlot 則用於記錄、統計不一樣緯度的 runtime 指標監控信息;
SystemSlot 則經過系統的狀態,例如 load1 等,來控制總的入口流量;
AuthoritySlot 則根據配置的黑白名單和調用來源信息,來作黑白名單控制;
FlowSlot 則用於根據預設的限流規則以及前面 slot 統計的狀態,來進行流量控制;
DegradeSlot 則經過統計信息以及預設的規則,來作熔斷降級;
Slot只綁定在CtEntry上
功能槽處理鏈,entry進入一個槽能夠添加本身的動做,以後後fire動做會entry下一個槽,exit同理
注意,實現上相同資源共享一個ProcessorSlotChain ,能夠跨Context
流控規則中的 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 。
我的公衆號:啊駝