Sentinel學習(二) —— 重要概念

Sentinel 中有很多比較重要的概念,我們要了解一個框架,首先要對框架中重要的概念實體進行分析,本文我將跟大家一起來分析一下 Sentinel 中非常重要的幾個概念。

Resource

Resource 是 Sentinel 中最重要的一個概念, Sentinel 通過資源來保護具體的業務代碼或其他後方服務。 Sentinel 把複雜的邏輯給屏蔽掉了,用戶只需要爲受保護的代碼或服務定義一個資源,然後定義規則就可以了,剩下的通通交給 Sentinel 來處理了。並且資源和規則是解耦的,規則甚至可以在運行時動態修改。定義完資源後,就可以通過在程序中埋點來保護你自己的服務了,埋點的方式有兩種:

  • try-catch 方式(通過 SphU.entry(…)),當 catch 到BlockException時執行異常處理(或fallback)
  • if-else 方式(通過 SphO.entry(…)),當返回 false 時執行異常處理(或fallback)

以上這兩種方式都是通過硬編碼的形式定義資源然後進行資源埋點的,對業務代碼的侵入太大,從0.1.1版本開始, Sentinel 加入了註解的支持,可以通過註解來定義資源,具體的註解爲:SentinelResource 。通過註解除了可以定義資源外,還可以指定 blockHandlerfallback 方法。

在 Sentinel 中具體表示資源的類是:ResourceWrapper ,他是一個抽象的包裝類,包裝了資源的 Name 和EntryType。他有兩個實現類,分別是:StringResourceWrapperMethodResourceWrapper

顧名思義,StringResourceWrapper 是通過對一串字符串進行包裝,是一個通用的資源包裝類,MethodResourceWrapper 是對方法調用的包裝。

在這裏插入圖片描述

Slot

Slot 是另一個 Sentinel 中非常重要的概念, Sentinel 的工作流程就是圍繞着一個個插槽所組成的插槽鏈來展開的。需要注意的是每個插槽都有自己的職責,他們各司其職完好的配合,通過一定的編排順序,來達到最終的限流降級的目的。默認的各個插槽之間的順序是固定的,因爲有的插槽需要依賴其他的插槽計算出來的結果才能進行工作。

但是這並不意味着我們只能按照框架的定義來,Sentinel 通過 SlotChainBuilder 作爲 SPI 接口,使得 Slot Chain 具備了擴展的能力。我們可以通過實現 SlotsChainBuilder 接口加入自定義的 slot 並自定義編排各個 slot 之間的順序,從而可以給 Sentinel 添加自定義的功能。

那SlotChain是在哪創建的呢?是在 CtSph.lookProcessChain() 方法中創建的,並且該方法會根據當前請求的資源先去一個靜態的HashMap中獲取,如果獲取不到纔會創建,創建後會保存到HashMap中。這就意味着,同一個資源會全局共享一個SlotChain。
在這裏插入圖片描述

Context

Context 上下文是 Sentinel 中一個比較難懂的概念。源碼中是這樣描述context類的:

This class holds metadata of current invocation

就是說在context中維護着當前調用鏈的元數據,那元數據有哪些呢,從context類的源碼中可以看到有:

  • entranceNode:當前調用鏈的入口節點
  • curEntry:當前調用鏈的當前entry
  • node:與當前entry所對應的curNode
  • origin:當前調用鏈的調用源

在這裏插入圖片描述

每次調用 SphU.entry() 或 SphO.entry() 都需要在一個 context 中執行,如果沒有當前執行時還沒有 context,那麼框架會使用默認的 context,默認的 context 是通過 MyContextUtil.myEnter() 創建的。

那如果我想自己在調用 SphU.entry() 或 SphO.entry() 前,自己創建一個context該怎麼操作呢?那可以通過調用 ContextUtil.enter() 方法來創建。

另外context是保存在ThreadLocal中的,每次執行的時候會優先到ThreadLocal中獲取。如果context爲null時纔會再次去創建一個context。

那什麼時候context會被置爲null並從ThreadLocal中清空呢?當Entry執行exit方法時,噹噹前entry的parent爲null時,也就說明當前entry是最上層的節點了,此時要把保存在ThreadLocal中的context也清空掉。

在NodeSelectorSlot類中有一個Map保存了DefaultNode,但是key是用的contextName,而不是resourceName,這是爲什麼呢?

試想一下,如果用resourceName來做map的key,那對於同一個資源resourceA來說,在context1中獲取到的defaultNodeA和在context2中獲取到的defaultNodeA是同一個,那麼怎麼在這兩個context中對defaultNodeA進行更改呢,修改了一個必定會對另一個產生影響。

而如果用contextName來作爲key,那對於同一個資源resourceA來說,在context1中獲取到的是defaultNodeA1,在context2中獲取到是defaultNodeA2,那在不同的context中對同一個資源可以使用不同的DefaultNode進行分別統計和計算,最後再通過ClusterNode進行合併就可以了。

所以在NodeSelectorSlot這個類裏面,map裏面保存的是contextName和DefaultNode的映射關係,目的是爲了可以在不同的context對相同的資源進行分開統計。

同一個context中對同一個resource進行多次entry()調用時,會形式一顆調用樹,這個樹是通過CtEntry之間的parent/child關係維護的。

Entry

Entry 是 Sentinel 中用來表示是否通過限流的一個憑證,就像一個token一樣。每次執行 SphU.entry() 或 SphO.entry() 都會返回一個 Entry 給調用者,意思就是告訴調用者,如果正確返回了 Entry 給你,那表示你可以正常訪問被 Sentinel 保護的後方服務了,否則 Sentinel 會拋出一個BlockException(如果是 SphO.entry() 會返回false),這就表示調用者想要訪問的服務被保護了,也就是說調用者本身被限流了。

entry中保存了本次執行 entry() 方法的一些基本信息,包括:

  • createTime:當前Entry的創建時間,主要用來後期計算rt
  • node:當前Entry所關聯的node,該node主要是記錄了當前context下該資源的統計信息
  • origin:當前Entry的調用來源,通常是調用方的應用名稱,在 ClusterBuilderSlot.entry() 方法中設置的
  • resourceWrapper:當前Entry所關聯的資源

當在一個上下文中多次調用了 SphU.entry() 方法時,就會創建一個調用樹,這個樹的節點之間是通過parent和child關係維持的。

需要注意的是:parent和child是在 CtSph 類的一個私有內部類 CtEntry 中定義的,CtEntry 是 Entry 的一個子類。 由於context中總是保存着調用鏈樹中的當前入口,所以噹噹前entry執行exit退出時,需要將parent設置爲當前入口。
在這裏插入圖片描述

Node

Node 中保存了資源的實時統計數據,例如:passQps,blockQps,rt等實時數據。正是有了這些統計數據後, Sentinel 才能進行限流、降級等一系列的操作。

node是一個接口,他有一個實現類:StatisticNode,但是StatisticNode本身也有兩個子類,一個是DefaultNode,另一個是ClusterNode,DefaultNode又有一個子類叫EntranceNode。

其中entranceNode是每個上下文的入口,該節點是直接掛在root下的,是全局唯一的,每一個context都會對應一個entranceNode。另外defaultNode是記錄當前調用的實時數據的,每個defaultNode都關聯着一個資源和clusterNode,有着相同資源的defaultNode,他們關聯着同一個clusterNode。

在這裏插入圖片描述

Metric

Metric 是 Sentinel 中用來進行實時數據統計的度量接口,node就是通過metric來進行數據統計的。而metric本身也並沒有統計的能力,他也是通過Window來進行統計的。

Metric有一個實現類:ArrayMetric,在ArrayMetric中主要是通過一個叫WindowLeapArray的對象進行窗口統計的。

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章