限流系列:sentinel

目錄

滑動窗口算法

Sentinel

數據模型

示例

大致流程

???????entry

???????entryWithPriority

???????FlowSlot.entry

???????checkFlow

???????canPass

???????avgUsedTokens

???????passQps

???????pass

???????currentWindow

calculateTimeIdx

???????calculateWindowStart

???????values


滑動窗口算法

? ? ? ?滑動窗口算法是將時間周期分為n個小周期,分別記錄每個小周期內的訪問次數,并且根據時間滑動刪除過期的小周期。

? ? ? ?如下圖,假設時間周期為1min,將1min再分割成2個小周期,統計每個小周期的訪問數量,則可以看到,第一個時間周期內訪問數量為75,第二個時間周期內訪問數量為100,超過100的數量被限流掉了。


? ? ? ?由此可見,當滑動窗口格子劃分得越多,那么滑動窗口的滾動就越平滑,限流的統計就越精確。可以很好的解決固定窗口的流動問題。

Sentinel

? ? ? ?滑動窗口算法也是Sentinel的默認算法。

???????數據模型

英文名稱

中文名稱

備注

array

窗口數組

windowLengthInMs

單個窗口時間長度

sampleCount

總窗口數量

intervalInMs

時間窗口總長度

sampleCount * windowLengthInMs

示例

public static void main(String[] args) throws Exception {
????//加載流控規則
????initFlowRules();
????for (int i = 0; i < 5; i++) {
????????Thread.sleep(200);
????????Entry entry = null;
????????try {
????????????entry = SphU.entry("sayHello");
????????????//被保護的邏輯
????????????log.info("訪問sayHello資源");
????????} catch (Exception ex) {
????????????log.info("被流量控制了,可以進行降級處理");
????????} finally {
????????????if (entry != null) {
????????????????entry.exit();
????????????}
????????}
????}
}
private static void initFlowRules() {
????List<FlowRule> rules = new ArrayList<>();
????//?創建一個流控規則
????FlowRule rule = new FlowRule();
????//?對sayHello這個資源限流
????rule.setResource("sayHello");
????//?基于qps限流
????rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
????//?qps最大為2,超過2就要被限流
????rule.setCount(2);
????rules.add(rule);
????//?設置規則
????FlowRuleManager.loadRules(rules);
}

大致流程

????點擊示例里的entry()方法,如下所示:

???????entry

public Entry entry(ResourceWrapper resourceWrapper, int count, Object... args) throws BlockException {
????return entryWithPriority(resourceWrapper, count, false, args);
}

? ? ? ? 點擊entryWithPriority()方法,如下所示:

???????entryWithPriority

private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)?throws BlockException {
????.. ...
????Entry e = new CtEntry(resourceWrapper, chain, context);
????try {
????????chain.entry(context, resourceWrapper, null, count, prioritized, args);
????} catch (BlockException e1) {
????????e.exit(count, args);
????????throw e1;
????} catch (Throwable e1) {
????????// This should not happen, unless there are errors existing in Sentinel internal.
????????RecordLog.info("Sentinel unexpected exception", e1);
????}
????return e;
}

? ? ? ?點擊chain.entry()方法,因為我們這次探究的是限流算法,所以選擇FlowSlot類,如下所示:

???????FlowSlot.entry

public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,?boolean prioritized, Object... args) throws Throwable {
????checkFlow(resourceWrapper, context, node, count, prioritized);
????fireEntry(context, resourceWrapper, node, count, prioritized, args);
}

? ? ? ? 點擊checkFlow()方法,如下所示:

???????checkFlow

public void checkFlow(Function<String, Collection<FlowRule>> ruleProvider, ResourceWrapper resource,?Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {

????????if (ruleProvider == null || resource == null) {

????????????return;

????????}

????????Collection<FlowRule> rules = ruleProvider.apply(resource.getName());

????????if (rules != null) {

????????????for (FlowRule rule : rules) {

????????????????if (!canPassCheck(rule, context, node, count, prioritized)) {

????????????????????throw new FlowException(rule.getLimitApp(), rule);

????????????????}

????????????}

????????}

}

???????canPass

public boolean canPass(Node node, int acquireCount, boolean prioritized) {
????int curCount = avgUsedTokens(node);
????if (curCount + acquireCount > count) {
????????... ...
????????return false;
????}
????return true;
}

? ? ? ?在這里,獲取已經使用的token數量,加上待申請的數量,如果超過流控規則里設置的最大值,則返回false。

? ? ? ?點擊avgUsedTokens()方法,如下所示:

???????avgUsedTokens

private int avgUsedTokens(Node node) {

????????if (node == null) {

????????????return DEFAULT_AVG_USED_TOKENS;

????????}

????????return grade == RuleConstant.FLOW_GRADE_THREAD ? node.curThreadNum() : (int)(node.passQps());

}

? ? ? ?在這里,通過調用node.passQps()獲取已經使用的token數量。

? ? ? ?點擊passQps(),如下所示:

???????passQps

public double passQps() {
????return rollingCounterInSecond.pass() / rollingCounterInSecond.getWindowIntervalInSec();
}

? ? ? ?在這里,rollingCounterInSecond對象保存了時間窗口對象的數組。pass()方法可以獲取每個有效的時間窗口對象里已經使用的令牌數量,getWindowIntervalInSec()方法是時間窗口總長度,以秒為單位。兩者相除就可以已使用的QPS。

? ? ? ?點擊pass()方法,如下所示:

???????pass

public long pass() {

????????data.currentWindow();

????????long pass = 0;

????????List<MetricBucket> list = data.values();

????????for (MetricBucket window : list) {

????????????pass += window.pass();

????????}

????????return pass;

????}

? ? ? ?在這里,會調用currentWindow()刷新當前窗口信息,然后累加每個窗口的計數值作為當前計數周期的計數值。

? ? ? ?點擊currentWindow()方法,如下所示:

???????currentWindow

public WindowWrap<T> currentWindow(long timeMillis) {

????int idx = calculateTimeIdx(timeMillis);

????long windowStart = calculateWindowStart(timeMillis);

????while (true) {

????????WindowWrap<T> old = array.get(idx);

????????if (old == null) {

????????????// 創建新窗口

????????????WindowWrap<T> window = new WindowWrap<>(windowLengthInMs, windowStart, newEmptyBucket());

????????????if (array.compareAndSet(idx, null, window)) {

????????????????return window;

????????????}

????????} else if (windowStart == old.windowStart()) {

????????????// 命中當前有效窗口

????????????return old;

????????} else if (windowStart > old.windowStart()) {

????????????// 窗口已過期,重置并復用

????????????if (updateLock.tryLock()) {

????????????????try {

????????????????????return resetWindowTo(old, windowStart);

????????????????} finally {

????????????????????updateLock.unlock();

????????????????}

????????????}

????????}

????}

}

在這里:

  1. 獲取當前時間窗口的索引
  2. 獲取當前窗口的起始時間
  3. 根據當前時間窗口的索引,獲取時間窗口對象,如果時間窗口對象為空,則創建一個新時間窗口對象;如果已經存在時間窗口對象,則返回該對象;如果時間窗口對象已過期,則重置并復用。

calculateTimeIdx

public int calculateTimeIdx(long timeMillis) {

????long timeId = timeMillis / windowLengthInMs;

????return (int)(timeId % array.length());

}

? ? ? ?在這里,根據當前時間戳計算對應窗口索引。

???????calculateWindowStart

protected long calculateWindowStart(long timeMillis) {

????return timeMillis - timeMillis % windowLengthInMs;

}

? ? ? ?在這里,計算當前窗口的起始時間(對齊到窗口邊界)。

? ? ? ?點擊步驟pass的values()方法,如下所示:

???????values

public List<T> values() {
????return values(TimeUtil.currentTimeMillis());
}
public List<T> values(long timeMillis) {
????if (timeMillis < 0) {
????????return new ArrayList<T>();
????}
????int size = array.length();
????List<T> result = new ArrayList<T>(size);
????for (int i = 0; i < size; i++) {
????????WindowWrap<T> windowWrap = array.get(i);
????????if (windowWrap == null || isWindowDeprecated(timeMillis, windowWrap)) {
????????????continue;
????????}
????????result.add(windowWrap.value());
????}
????return result;
}

? ? ? ? 在這里,循環時間窗口數組,忽略已經失效的時間窗口對象,將有效的時間窗口對象保存在一個列表對象里,并作為方法返回值進行返回。

???????isWindowDeprecated

public boolean isWindowDeprecated(long time, WindowWrap<T> windowWrap) {
????return time - windowWrap.windowStart() > intervalInMs;
}

? ? ? ? 在這里,將當前時間減去指定時間窗口對象的起始時間,如果結果大于計數周期時長,則表明指定的時間窗口對象已經失效。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/81378.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/81378.shtml
英文地址,請注明出處:http://en.pswp.cn/web/81378.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java 訪問者模式深度重構:從靜態類型到動態行為的響應式設計實踐

一、訪問者模式的本質與核心價值 在軟件開發的漫長演進中&#xff0c;設計模式始終是架構師手中的利刃。當我們面對復雜對象結構上的多種操作需求時&#xff0c;訪問者模式&#xff08;Visitor Pattern&#xff09;猶如一把精密的手術刀&#xff0c;能夠優雅地分離數據結構與作…

UE 5 C++設置物體位置和旋轉,初始化虛幻引擎樣條線、加載引用虛幻編輯器中的藍圖、設置虛幻編輯器中Actor大小

一、設置物體位置和旋轉 UE.cpp文件中代碼&#xff1a; Mesh->SetWorldLocationAndRotation(FVector(50.0f, 50.0f, 50.0f),FRotator(0,-90,0)); vs代碼編輯器中旋轉信息順序&#xff08;yzx&#xff09;&#xff1a; Pitch、 Yaw、 Roll UE編輯器中旋轉信息順序&#xf…

【文本分類】KG-HTC 知識圖譜提升分類準確率

最近看到一篇論文“KG-HTC: Integrating Knowledge Graphs into LLMs for Effective Zero-shot Hierarchical Text Classification”&#xff0c;介紹了文本分類的技巧&#xff0c;這篇文航主要利用了知識圖譜大模型的思路&#xff0c;實驗效果不錯&#xff0c;里面的一些論述也…

三大微調技術對比:Prompt/Prefix/P-Tuning

Prompt Tuning、Prefix Tuning和P - Tuning的區別 概念方面: Prompt Tuning:在輸入序列前添加可訓練的額外Token以適配下游任務,預訓練語言模型參數不變。比如在文本分類中,在句子前加特定Token如“(OPINION)”,讓模型理解是對觀點進行分類的任務。Prefix Tuning:在每層T…

14.「實用」扣子(coze)教程 | Excel文檔自動批量AI文檔生成實戰,中級開篇

隨著AI編程工具及其能力的不斷發展&#xff0c;編程將變得越來越簡單。 在這個大趨勢下&#xff0c;大師兄判斷未來的編程將真正成為像office工具一樣的辦公必備技能。每個人通過 &#xff08;專業知識/資源編程&#xff09;將自己變成一個復合型的人才&#xff0c;大大提高生…

量子-經典協同計算新路徑:NISQ 時代混合算法對后量子密碼學的適應性探索

內容來源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨浪味仙 排版丨浪味仙 行業動向&#xff1a;3700字丨10分鐘閱讀 5 月 20 日&#xff0c;由北京量子院、清華大學、數學工程與先進計算國家重點實驗室、南洋理工大學、量子信息前沿科學中心…

CentOS中安裝Docker Compose

在CentOS中安裝Docker Compose的步驟如下&#xff1a; 步驟 1&#xff1a;確保Docker已安裝 Docker Compose依賴Docker環境&#xff0c;請先安裝Docker&#xff1a; # 添加Docker官方倉庫 sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://downlo…

電商小程序店鋪詳情頁:頭部無限分類與篩選功能實現

電商小程序店鋪詳情頁:頭部無限分類與篩選功能實現 一、場景需求與技術選型二、頭部無限分類導航三、篩選功能實現:Picker多列選擇組件一、場景需求與技術選型 在電商小程序生態中,店鋪詳情頁作為用戶瀏覽商品的核心流量入口,其交互效率與功能完整性直接影響商品轉化率。傳…

Graph Neural Network(GNN)

我們首先要了解什么是圖,圖是由節點和邊組成的,邊的不一樣也導致節點的不同(參考化學有機分子中的碳原子) gnn可以處理classification的問題,也就是分類的問題 也可以處理generation的問題 借一部日劇來說明,這個日劇是講主角尋找殺害他父親的兇手的,劇中的人物有姓名和特征 …

FallbackHome的啟動流程(android11)

首次開機開機動畫播完進入Launcher桌面時黑屏進入Launcher,有黑屏不太美觀&#xff0c;在重啟以后會在進入桌面后會顯示android正在啟動等一會進入Launcher,這就是系統FallBackHome機制 接下來我們跟著代碼看下首次啟動系統如何進入FallbackHome的 在SystemServer的startOthe…

【EdgeYOLO】《EdgeYOLO: An Edge-Real-Time Object Detector》

Liu S, Zha J, Sun J, et al. EdgeYOLO: An edge-real-time object detector[C]//2023 42nd Chinese Control Conference (CCC). IEEE, 2023: 7507-7512. CCC-2023 源碼&#xff1a;https://github.com/LSH9832/edgeyolo 論文&#xff1a;https://arxiv.org/pdf/2302.07483 …

宮格導航--純血鴻蒙組件庫AUI

摘要&#xff1a; 宮格導航(A_GirdNav)&#xff1a;可設置導航數據&#xff0c;建議導航項超過16個&#xff0c;可設置“更多”圖標指向的頁面路由。最多顯示兩行&#xff0c;手機每行最多顯示4個圖標&#xff0c;折疊屏每行最多6個圖標&#xff0c;平板每行最多8個圖標。多余圖…

調試的按鈕

在Debug的時候&#xff0c;會有一些按鈕&#xff0c;我們需要知道它們各自的作用。 注&#xff1a;調試器本身并沒有一個直接的、可以撤銷已執行代碼效果的“返回上一步&#xff08;Undo Last Step&#xff09;”或“逆向執行&#xff08;Reverse Debugging&#xff09;”按鈕…

人工智能如何協助老師做課題

第一步&#xff1a;在騰訊元寶對話框中輸入如何協助老師做課題&#xff0c;通過提問&#xff0c;我們了解了老師做課題的步驟和建議。 第二步&#xff1a;開題報告提問&#xff0c;騰訊元寶對話框中&#xff0c;輸入“大單元視域下小學數學教學實踐研究課題開題報告。”......…

OpenGL Chan視頻學習-5 Vertex Attributes and Layouts in OpenGL

bilibili視頻鏈接&#xff1a; 【最好的OpenGL教程之一】https://www.bilibili.com/video/BV1MJ411u7Bc?p5&vd_source44b77bde056381262ee55e448b9b1973 一、知識點整理 1.1.OpenGL管線工作流程 為顯卡提供繪制的所有數據&#xff0c;并將數據存儲在GPU內存使用著色器&…

Linux_編輯器Vim基本使用

?? 歡迎大家來到小傘的大講堂?? &#x1f388;&#x1f388;養成好習慣&#xff0c;先贊后看哦~&#x1f388;&#x1f388; 所屬專欄&#xff1a;LInux_st 小傘的主頁&#xff1a;xiaosan_blog 制作不易&#xff01;點個贊吧&#xff01;&#xff01;謝謝喵&#xff01;&a…

MyBatis 高級映射功能詳解:處理復雜數據庫關系

MyBatis 的高級映射功能是其強大特性之一&#xff0c;它允許開發者輕松處理數據庫中的復雜關系&#xff0c;如一對一、一對多和多對多關系。本文將深入探討這些高級映射功能&#xff0c;包括映射配置方法、嵌套查詢和關聯查詢的使用&#xff0c;并通過示例代碼進行演示。 1.數據…

Halo:一個強大易用的國產開源建站工具

Halo 是一款國產開源的建站工具&#xff0c;適合快速搭建博客、論壇、知識庫、公司官網等多種類型的網站&#xff0c;目前在 GitHub 上已經獲得了 35.6k Star。 功能特性 Halo 核心功能與優勢包括&#xff1a; 插件架構&#xff1a;Halo 采用可插拔架構&#xff0c;功能模塊之…

Java-ArrayList集合的遍歷方式詳解

Java-ArrayList集合的遍歷方式詳解 二、ArrayList概述三、ArrayList的遍歷方式1. 普通for循環遍歷2. 增強for循環遍歷3. 迭代器遍歷4. ListIterator遍歷5. Java 8 Stream API遍歷 四、性能對比與分析性能測試結果分析 五、遍歷方式的選擇建議六、常見遍歷陷阱與注意事項1. 并發…

華為網路設備學習-23(路由器OSPF-LSA及特殊詳解 二)

OSPF動態路由協議要求&#xff1a; 1.必須有一個骨干區域&#xff08;Area 0&#xff09;。有且僅有一個&#xff0c;而且連續不可分割。 2.所有非骨干區域&#xff08;Area 1-n&#xff09;必須和骨干區域&#xff08;Area 0&#xff09;直接相連&#xff0c;且所有區域之間…