概述
在接連寫了兩篇關於限流的文章(《面試補習》- 你來說說什麼是限流?, 限流神器Sentinel,不瞭解一下嗎?)後,總感覺還差最後一點內容來閉環
整個限流相關
的內容,這兩天在翻查相關文章後,找到了一篇通俗易懂
的 《Sentinel 解密》 文章,感謝 寒舟
大佬提供的幫助~ 將這部分內容補全。
1、Sentinel 概念
1.1、程式碼結構
1.2、核心概念
Resource
資源是 Sentinel
對所保護的內容的抽象,任何想保護的程式碼
、函式
等都可以通過 SphU.entry 介面將其定義為一個資源,SphU.entry
介面的第一個引數描述了該資源的名稱
Node
Node是sentinel中用來儲存統計資料的記憶體結構,以樹形結構和hash儲存:
一個resource
按照不同入口設定不同統計結點,儲存context
級別的統計資訊;一個resource
下設定統一的cluster node
,儲存resource粒度的統計資訊;每個cluster node
根據來源下掛不同origin node
,儲存各個來源的統計資訊。
配置中不同限流模式其實最終對應的就是選擇不同的node進行計算:
- 直接模式: 選擇cluster node
- 關聯模式: 選擇關聯resource的cluster node
- 應用淶源: 選擇origin node
- 鏈路模式: 選擇default node
Node 型別:
- StatisticNode:最為基礎的統計節點,是後續三種節點的父類,包含秒級和分鐘級兩個滑動視窗結構。
- DefaultNode:鏈路節點,用於統計呼叫鏈路上某個資源的資料,維持樹狀結構。
- ClusterNode:簇點,用於統計每個資源全域性的資料(不區分呼叫鏈路),以及存放該資源的按來源區分的呼叫資料
- EntranceNode:入口節點,特殊的鏈路節點,對應某個 Context 入口的所有呼叫資料。
Context
Context
是對資源操作時的上下文環境, 同一個執行緒中通過thread local
進行傳裡, context
中維護了currentEntry
, 同一個執行緒中可以多次entry/exit
, 所以entry
同樣維護了一個樹狀結構:
Entry
呼叫點,這裡面儲存著和某一特定資源的某一次呼叫相關的資訊,比如這次呼叫的起始執行時間,結束時間,是否丟擲異常,呼叫鏈路中的父子關係,該呼叫點對應資源的處理鏈(ProcessorSlotChain
),還儲存了該呼叫點所對應的資源統計節點
Slot
slot
是規則處理器,通過責任鏈模式進行串聯處理。
1.3、核心類圖
sentinel主要通過Node實現結點維護統計資訊、通過LeapArray實現滑動視窗演算法。
2、Sentinel 流程
2.1、規則配置
應用通過訂閱監聽模式實現各種監聽器,監聽然後對應不同配置的進行更新,圖中列出了幾個主要監聽器,如: 規則配置、基本配置、叢集配置等。
2.2、規則驗證
應用呼叫entry入口即啟動了規則驗證流程,主要步驟為獲取context, 構建並執行slot chain,如果違反規則紀錄失敗計數,清理contxt;反之,則紀錄成功計數,返回驗證成功。
3、Sentinel 核心
3.1、限流演算法
我們所熟知的限流演算法包括: 1. 計數器 2. 固定視窗 3. 滑動視窗 4.漏桶 5.令牌桶等。
- 1、
計數器
,維護一個計數器,訪問進入計數器+1,如果超過闕值則拒絕、訪問結束計數器-1。 - 2、
固定視窗
,給定一個時間視窗,維護一個計數器、如果視窗內訪問次數大於闕值則拒絕。 - 3、
滑動視窗
,固定視窗有臨界問題,所以提出滑動視窗思想,將一個大的時間視窗切分成更細粒度的子視窗,每個子視窗單獨統計,每過一個子視窗的時間,就向右滑動一個子視窗。 - 4、
漏桶
,滑動視窗不能解決平滑度問題,為了解決平滑度問題,提出漏桶演算法進行流速控制。 - 5、
令牌桶
: 跟漏桶演算法相反,以很定的速度產生令牌,桶滿則丟棄,請求訪問時從桶裡後去令牌,如果能夠獲取,則通過否則拒絕。
Sentinel
中主要使用了計數器
、滑動視窗
、漏桶
等限流思想
。比如對於執行緒數的流控
,就是直接使用計數器
進行限制,對於QPS的限制則
使用了滑動視窗
的思想,滑動視窗
也是Sentinel
的主要流控實現方式。
3.2、滑動視窗
Sentinel
支援秒級的視窗,預設視窗大小1秒
,假設分為5個子視窗
,則每個子視窗的大小為200ms
, 如上圖,每個格子有個索引timeId
, 那視窗是如何進行滑動呢?演算法的關鍵實現就是在前面類圖提到過的currentWindow()
方法:
- 1、根據當前時間戳計算timeId
- 2、根據timdId獲取當前子視窗
- 3、如果子視窗不存在,則建立子視窗
- 4、如果子視窗已經存在,則根據當前時間計算開始開始時間,對比子視窗的開始時間,如果開始時間一致,則說明此子視窗即當前視窗,如果大於子視窗時間,則需要滑動
- 5、滑動其實就是resetWidow, 包括reset windowStartTime, window中的統計資訊等,如上圖所示
3.3、優先順序
優先順序的目的是為了區分正常流量
和其他低優先順序流量
,比如壓測流量
。對於正常流量
,如果超過闕值
,不想直接拒絕,嘗試借用將來視窗的指標,如果借用成功,則請求通過,如果借用失敗,則拒絕。當時間視窗
真正走到將來的時間點時,需要將借用視窗的計數器
恢復設定。
那如何當前請求是否可以借用呢?如下圖,主要維護一個借用時間闕值,計算一秒視窗內的總數有沒有超過QPS,如果沒有超過看借用時間是否超過借用闕值:
3.4、熱點引數
一般資源的定義需要可列舉的,不能爆炸式的,假如對每個userId進行限流,這樣會導致sentinel的儲存空間爆炸式增長,最後導致OOM. 對於這種細粒度的引數限流,sentinel提供了熱點引數方案。
如圖:
熱點引數就是在正常統計資訊而外增加了引數統計,一樣支援執行緒數和QPS兩種限流方式,對於執行緒數,每個引數索引維護了一個LRU cache; 對於QPS,每個引數索引、每個子視窗、每個統計緯度維護了一個LRU cache, cache中維護了各種計數。通過LRU cache可以避免記憶體爆炸, 達到熱點引數限流的目的。
點關注,不迷路
好了各位,以上就是這篇文章的全部內容了,我後面會每週都更新幾篇高質量的大廠面試和常用技術棧相關的文章。感謝大夥能看到這裡,如果這個文章寫得還不錯, 求三連!!! 創作不易,感謝各位的支援和認可,我們下篇文章見!
我是 九靈
,有需要交流的童鞋可以 加我wx,Jayce-K
,關注公眾號:Java 補習課
,掌握第一手資料!
如果本篇部落格有任何錯誤,請批評指教,不勝感激 !