Sentinel 原理-實體類

weixin_34148340發表於2018-11-09

逅弈 轉載請註明原創出處,謝謝!

系列文章

Sentinel 原理-全解析
Sentinel 原理-呼叫鏈
Sentinel 原理-滑動視窗
Sentinel 實戰-限流篇
Sentinel 實戰-控制檯篇
Sentinel 實戰-規則持久化
Sentinel 實戰-叢集限流篇

Sentinel 系列教程,現已上傳到 github 和 gitee 中:

5417792-a2e73a7ca4dda700.png
sentinel-tutorial.png

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 ,他是一個抽象的包裝類,包裝了資源的 NameEntryType。他有兩個實現類,分別是:StringResourceWrapperMethodResourceWrapper

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

5417792-82808173410b5a88.png
resource.png

Slot

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

但是這並不意味著我們只能按照框架的定義來,sentinel 通過 SlotChainBuilder 作為 SPI 介面,使得 Slot Chain 具備了擴充套件的能力。我們可以通過實現 SlotsChainBuilder 介面加入自定義的 slot 並自定義編排各個 slot 之間的順序,從而可以給 sentinel 新增自定義的功能。

那SlotChain是在哪建立的呢?是在 CtSph.lookProcessChain() 方法中建立的,並且該方法會根據當前請求的資源先去一個靜態的HashMap中獲取,如果獲取不到才會建立,建立後會儲存到HashMap中。這就意味著,同一個資源會全域性共享一個SlotChain

5417792-42bbbac532927898.png
slot-chain.png

Context

context上下文是sentinel中一個比較難懂的概念。原始碼中是這樣描述context類的:

This class holds metadata of current invocation

就是說在context中維護著當前呼叫鏈的後設資料,那後設資料有哪些呢,從context類的原始碼中可以看到有:

  • entranceNode:當前呼叫鏈的入口節點
  • curEntry:當前呼叫鏈的當前entry
  • node:與當前entry所對應的curNode
  • origin:當前呼叫鏈的呼叫源
5417792-c57a61d4a1c68e46.png
context.png

每次呼叫 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進行更改呢,修改了一個必定會對另一個產生影響。

5417792-c2c986e29379ade7.png
resource-node.png

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

5417792-0c0a7ec2694f7506.png
content-node.png

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

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

具體的呼叫鏈的原理分析可以參考筆者的另一篇文章:限流降級神器-哨兵(sentinel)的資源呼叫鏈原理分析

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 中定義的,CtEntryEntry 的一個子類。
由於context中總是儲存著呼叫鏈樹中的當前入口,所以噹噹前entry執行exit退出時,需要將parent設定為當前入口。

5417792-70c407d5dde36b31.png
entry.png

Node

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

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

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

5417792-ace3d1297df2d282.png
node.png

Metric

metric是sentinel中用來進行實時資料統計的度量介面,node就是通過metric來進行資料統計的。而metric本身也並沒有統計的能力,他也是通過Window來進行統計的。

具體的統計原理,可以參考筆者另一篇文章:sentinel基於滑動時間視窗的實時指標統計原理分析

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

5417792-0c26b0016a95c8ed.png
metric.png
5417792-f0b1dcdbbfebcbad.jpg
更多原創好文,請關注「逅弈逐碼」

相關文章