文章目錄
- 前言
- 一、客戶端與Spring Boot整合
- 二、SphU.entry
- 2.1、構建責任鏈
- 2.2、調用責任鏈
- 2.2.1、NodeSelectorSlot
- 2.2.2、ClusterBuilderSlot
- 2.2.3、LogSlot
- 2.2.4、StatisticSlot
- 2.2.5、AuthoritySlot
- 2.2.6、SystemSlot
- 2.2.7、FlowSlot
- 2.2.7.1、selectNodeByRequesterAndStrategy
- 2.2.7.2、canPass
- 2.2.8、DegradeSlot
- 總結
前言
??Sentinel作為Spring cloud alibaba中流控的組件,在微服務架構中也有廣泛的應用。其核心源碼主要體現在客戶端。客戶端在啟動時,和Nacos類似,也會將自己的信息注冊到服務端。而服務端的頁面上配置各種規則時,實際上也是將信息發送到了客戶端。
??我們最常使用的@SentinelResource
注解:
??底層也是基于AOP + 責任鏈模式實現的。**Sentinel的難點不在于處理流程,而在于限流的算法。**本篇僅介紹Sentinel責任鏈的核心流程。
一、客戶端與Spring Boot整合
??在Spring Boot項目中,如果需要引入Sentinel,通常需要在pom文件中加入:
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>
??該組件也是利用了Spring Boot的自動配置:
??其中的SentinelAutoConfiguration
是核心:
??在SentinelAutoConfiguration
中,會注冊SentinelResourceAspect
Bean:
??SentinelResourceAspect
實際上是一個切面,匹配了所有加入了@SentinelResource
注解的方法:
??invokeResourceWithSentinel
是一個環繞通知。。在執行目標方法之前,首先會得到目標方法對象,以及處理注解中的一些信息,然后調用SphU.entry
方法。該方法是Sentinel流程的核心。
??點擊進去,會調用到entry
方法:
??Env在實例化之前,會觸發static
中的邏輯:
??在doInit
方法中,又會通過SPI機制,加載InitFunc
中的類:
??其中的HeartbeatSenderInitFunc
是定期向服務端發送心跳的:
??CommandCenterInitFunc
,是將客戶端各種接收規則的接口信息,暴露給服務端:
??上面的邏輯,包括后續構建,執行責任鏈,是在切面中,并非是在應用啟動時執行的,而是在執行加入了@SentinelResource
注解的方法時才會去執行!
二、SphU.entry
??SphU.entry
方法內部主要做了兩件事:
- 構建責任鏈。
- 按照順序依次調用責任鏈。
2.1、構建責任鏈
??在進入lookProcessChain
方法后,首先通過雙檢鎖模式,判斷當前加入了@SentinelResource
注解的方法,是否已經為其構建過責任鏈,如果沒有,才會執行newSlotChain
方法。也就是說,是每一個加入了注解的方法,都有一個對應的責任鏈,并且只在應用啟動后該方法第一次被調用時初始化。
??最終調用的是DefaultSlotChainBuilder
的build
方法:
??在該方法中,主要做了兩件事:
??通過SPI機制,加載ProcessorSlots
文件中的類(責任鏈中的具體組成類)。
??真正地去構建責任鏈:在執行ProcessorSlotChain chain = new DefaultProcessorSlotChain();
這一段代碼時,實際上是構造了:
??構造出的是下圖的數據結構,end是指向first的引用:
??在構造完成后,就會利用chain.addLast((AbstractLinkedProcessorSlot<?>) slot);
方法,向上圖的數據結構中插入具體的責任鏈類了:
??首先將end的next指針指向NodeSelectorSlot,因為end是指向指向first的引用,實際上first的next指針也指向了NodeSelectorSlot:
??然后將end指向NodeSelectorSlot:
??以此類推,最終構建出的責任鏈是:
圖片來源:圖靈學院
2.2、調用責任鏈
??這里我們重點關注FlowSlot和DegradeSlot
,它們是sentinel核心功能-限流熔斷降級的體現。
2.2.1、NodeSelectorSlot
??首先調用的是NodeSelectorSlot
,它的作用是構建資源調用的統計節點,用于記錄調用鏈路信息,并且將資源關聯到相應的 DefaultNode。
??fireEntry
就是在滿足條件的情況下,繼續調用后續的責任鏈。
2.2.2、ClusterBuilderSlot
??ClusterBuilderSlot
和NodeSelectorSlot
是類似的,它的作用是構建統計節點的聚合關系。但是NodeSelectorSlot 是按調用鏈統計,ClusterBuilderSlot 是按資源維度統計。
2.2.3、LogSlot
??LogSlot
的作用,是在后續的責任鏈調用過程中出現異常時,進行日志的記錄,體現在它的try…catch中:
2.2.4、StatisticSlot
??StatisticSlot
是先將請求放行到后續的責任鏈,在后續的責任鏈調用完成后,再去進行統計資源的調用情況的操作。例如記錄 QPS(每秒請求數)、RT(響應時間)、線程數、異常數等。
??包括拋出了各種異常之后的記錄,這些記錄都是執行降級、限流等控制的基礎數據來源。
2.2.5、AuthoritySlot
??AuthoritySlot
是進行授權規則的檢查,例如黑白名單:
??簡單回顧一下黑白名單的使用,首先需要在sentinel控制臺的授權規則選項卡
進行配置:
??這里的資源名
,是http請求的路徑,而流控應用
,可以是特定的ip,也可以是請求路徑:
@Component
public class IPLimiter implements RequestOriginParser {/*** 獲取當前服務實例的ip*/@Overridepublic String parseOrigin(HttpServletRequest httpServletRequest) {return httpServletRequest.getRemoteAddr();}
}
??在checkBlackWhiteAuthority
方法中,首先會獲取所有設置的規則,然后根據當前的資源名,獲取該資源對應的所有規則
??然后進行檢查:
??這里的黑白名單體現在RuleConstant
這個常量類中:
??判斷邏輯有點繞:
- 如果規則中的IP或路徑,和請求中的匹配,contain會為true,反之為false。
- 在黑名單的判定中,如果contain為true,則返回false,代表請求不通過。因為黑名單就是要對能和規則匹配上的請求進行攔截。
- 在白名單的判斷中,如果contain為false,則返回false,代表請求不通過。因為contain為false,代表請求和規則匹配不上,也就是不在白名單中。
2.2.6、SystemSlot
??SystemSlot
是對系統規則進行控制,包括系統整體的 QPS,平均響應時間(RT),當前系統的并發線程數等。
2.2.7、FlowSlot
??FlowSlot
是 Sentinel 的核心功能之一,用于流量控制(限流)規則判斷。
??同樣會獲取到控制臺設置的所有規則,然后逐個進行匹配:
??最終調用到的是passLocalCheck
,其中也有兩個關鍵方法:
2.2.7.1、selectNodeByRequesterAndStrategy
??selectNodeByRequesterAndStrategy
用于在執行限流時 選擇哪個節點(Node)來做統計和判斷。不同的來源(origin)和限流策略(strategy)決定了限流數據統計的維度。首先會獲取到流控模式,也就是控制臺設置的:
- 匹配指定 origin 的限流
- 如果是
直接模式
,就利用context.getOriginNode();
調用方自己的統計節點限流。 - 如果是其他策略,利用
selectReferenceNode
再次匹配:
- 如果是
- 如果來源是
default
:- 直接限流時,使用當前資源的全局統計節點
- 非直接限流,使用關聯資源的統計節點。
- 如果來源是
other
:- 直接限流時,使用當前資源的全局統計節點
- 非直接限流,使用關聯資源的統計節點。
2.2.7.2、canPass
??在拿到上一步推斷出的節點后,會調用canPass方法,這里的canPass也有不同的實現:
??對應控制臺中的:
??這里涉及到滑動窗口,令牌桶,漏桶算法,會在后續進行說明。 Sentinel的難點不在于流程,而是算法。
2.2.8、DegradeSlot
??DegradeSlot
的作用是熔斷降級控制。也是 Sentinel 的核心功能之一:
??在tryPass
方法中,會對逐條規則進行校驗,如果此時的斷路器處于打開狀態,
??并且超過了熔斷時間,會修改狀態為半開
。
??熔斷降級中有一個重要的概念,也就是斷路器
。在Sentinel 1.8 版本之后,斷路器有三種狀態,都記錄在CircuitBreaker
的內部State
枚舉類中:
??在這里簡單的說一下三種狀態的轉換:
- 正常情況下,斷路器處于
關閉
狀態,所有請求正常通過。 - 當請求觸發了降級條件(如異常比例過高、RT過大) 后,斷路器會進入
打開
狀態,在接下來的熔斷時長內(如 10 秒),所有請求都被拒絕(降級)。 - 當熔斷時長結束后,下一個請求到達時,斷路器進入
半開
狀態:- 如果該請求再次觸發降級條件,斷路器重新回到
打開
狀態。 - 如果該請求通過且正常,斷路器會恢復為
關閉
狀態。
- 如果該請求再次觸發降級條件,斷路器重新回到
Closed → [觸發降級條件] → Open → [熔斷時長結束,下一請求] → Half-Open
↑ ↓
└────── [探測失敗] ←──── [探測成功] ←──────────────┘
總結
??本篇介紹了Sentinel 實現控制臺功能,在服務端的實現原理:通過AOP + 責任鏈模式實現。并且在調用目標方法時,為每一個請求都創建一份責任鏈,放入緩存,依次調用。
??后面幾個責任鏈的實現,在規則校驗不通過時,都會拋出異常,而真正處理的邏輯,在StatisticSlot
的catch中,以及SentinelResourceAspect#invokeResourceWithSentinel
的entry.exit
中,包括處理斷路器的狀態。
下一篇:Sentinel核心源碼分析(下)