常見限流算法及實現

1. 固定窗口計數器(Fixed Window Counter)

  • 原理:在固定時間窗口(如1分鐘)內統計請求數,超過閾值則拒絕后續請求。
  • 優點:實現簡單,內存占用低。
  • 缺點:存在窗口切換時的流量突增問題(如相鄰窗口邊界處可能允許雙倍流量)。
/*** @description: 固定窗口限流* @Author: whopxx* @CreateTime: 2025-03-16*/
public class FixedWindowLimiter {private final int limit;      // 窗口內最大請求數private final long windowMs;  // 窗口時間(毫秒)private final AtomicInteger counter = new AtomicInteger(0);private volatile long windowStart = System.currentTimeMillis();public FixedWindowLimiter(int limit, long windowMs) {this.limit = limit;this.windowMs = windowMs;}public boolean tryAcquire() {long now = System.currentTimeMillis();if (now - windowStart > windowMs) {synchronized (this) {if (now - windowStart > windowMs) {windowStart = now;counter.set(0);}}}return counter.incrementAndGet() <= limit;}public static void main(String[] args) {FixedWindowLimiter fixedWindowLimiter = new FixedWindowLimiter(5, 1000);for (int i = 0; i < 100; i++) {boolean b = fixedWindowLimiter.tryAcquire();System.out.println(b);try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

2. 滑動窗口計數器(Sliding Window Counter)

  • 原理:將時間分割為更細粒度的子窗口(如每分鐘分為60個1秒窗口),統計最近完整時間窗口內的請求總數。
  • 優點:緩解固定窗口的臨界問題,限流更平滑。
  • 缺點:實現較復雜,需存儲子窗口的請求記錄。
/*** @description: 滑動窗口限流* @Author: whopxx* @CreateTime: 2025-03-16*/
public class SlidingWindowLimiter {private final long windowMs;          // 窗口總時間(毫秒)private final int subWindowCount;     // 子窗口數量private final long subWindowMs;       // 子窗口時間(毫秒)private final int limit;              // 窗口內最大請求數private final AtomicInteger[] counters;private final long[] windowStartTimes; // 每個子窗口的起始時間private final ReentrantLock lock = new ReentrantLock();public SlidingWindowLimiter(int limit, long windowMs, int subWindowCount) {this.limit = limit;this.windowMs = windowMs;this.subWindowCount = subWindowCount;this.subWindowMs = windowMs / subWindowCount;this.counters = new AtomicInteger[subWindowCount];this.windowStartTimes = new long[subWindowCount];for (int i = 0; i < subWindowCount; i++) {counters[i] = new AtomicInteger(0);windowStartTimes[i] = System.currentTimeMillis() - i * subWindowMs;}}public boolean tryAcquire() {long now = System.currentTimeMillis();lock.lock();try {// 1. 清理所有過期的子窗口int expiredCount = 0;for (int i = 0; i < subWindowCount; i++) {if (now - windowStartTimes[i] > windowMs) {expiredCount += counters[i].getAndSet(0);windowStartTimes[i] = now - (now % subWindowMs); // 對齊時間窗口}}// 2. 計算當前子窗口索引int currentSubIdx = (int) ((now % windowMs) / subWindowMs);// 3. 統計當前窗口內總請求數int total = expiredCount;for (int i = 0; i < subWindowCount; i++) {total += counters[i].get();}if (total >= limit) {return false;}// 4. 寫入當前子窗口counters[currentSubIdx].incrementAndGet();return true;} finally {lock.unlock();}}public static void main(String[] args) throws InterruptedException {// 測試:限制1秒內最多2次請求,窗口分為5個子窗口(每個200ms)SlidingWindowLimiter limiter = new SlidingWindowLimiter(2, 1000, 5);for (int i = 0; i < 10; i++) {System.out.println("Request " + (i + 1) + ": " + (limiter.tryAcquire() ? "OK" : "Limited"));Thread.sleep(150); // 模擬請求間隔150ms}}
}

3. 漏桶算法(Leaky Bucket)

  • 原理:請求像水一樣進入桶中,桶以固定速率“漏水”(處理請求),桶滿則拒絕新請求。
  • 優點:強制恒定速率處理,適合平滑突發流量。
  • 缺點:無法靈活應對突發流量(即使系統空閑時也無法瞬時處理大量請求)。
/*** @description: 漏桶限流器* @Author: whopxx* @CreateTime: 2025-03-16*/
public class LeakyBucketLimiter {private final long capacity;     // 桶容量private final long rate;         // 流出速率,每秒rate個private AtomicLong water = new AtomicLong(0);          // 當前水量private long lastLeakTime = System.currentTimeMillis();public LeakyBucketLimiter(long capacity, long rateMsPerReq) {this.capacity = capacity;this.rate = rateMsPerReq;}public synchronized boolean tryAcquire() {if (water.get() == 0){lastLeakTime = System.currentTimeMillis();water.set(1);return true;}water.set(water.get() - (System.currentTimeMillis() - lastLeakTime) / 1000 * rate);water.set(water.get() < 0 ? 0 : water.get());lastLeakTime += (System.currentTimeMillis() - lastLeakTime) / 1000 * 1000;if (water.get() >= capacity) {return false;} else {water.set(water.get() + 1);return true;}}public static void main(String[] args) {LeakyBucketLimiter limiter = new LeakyBucketLimiter(5, 1); // 容量為5,流出速率為1個/秒for (int i = 0; i < 100; i++) {boolean b = limiter.tryAcquire();System.out.println(b);try {Thread.sleep(200);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

4. 令牌桶算法(Token Bucket)

  • 原理:系統以固定速率生成令牌存入桶,請求需獲取令牌才能處理,無令牌時觸發限流。
  • 優點:允許突發流量(桶內積累的令牌可一次性使用),更靈活。
  • 缺點:需維護令牌生成邏輯,實現較復雜。
  • 典型應用:Guava的RateLimiter、Nginx限流模塊。
/*** @description: 令牌桶限流算法* @Author: whopxx* @CreateTime: 2025-03-16*/
public class TokenBucketLimiter {//桶的容量private final long capacity;//放入令牌的速率 每秒放入的個數private final long rate;//上次放置令牌的時間private static long lastTime = System.currentTimeMillis();//桶中令牌的余量private static AtomicLong tokenNum = new AtomicLong();public TokenBucketLimiter(int capacity, int permitsPerSecond) {this.capacity = capacity;this.rate = permitsPerSecond;tokenNum.set(capacity);}public synchronized  boolean tryAcquire() {//更新桶中剩余令牌的數量long now = System.currentTimeMillis();tokenNum.addAndGet((now - lastTime) / 1000 * rate);tokenNum.set(Math.min(capacity, tokenNum.get()));//更新時間lastTime += (now - lastTime) / 1000 * 1000;//桶中還有令牌就放行if (tokenNum.get() > 0) {tokenNum.decrementAndGet();return true;} else {return false;}}//測試public static void main(String[] args) {TokenBucketLimiter limiter = new TokenBucketLimiter(3, 2); // 允許每秒放入2個令牌,桶容量為5for (int i = 0; i < 100; i++) {if (limiter.tryAcquire()) {System.out.println("成功請求");} else {System.out.println("請求失敗");}try {Thread.sleep(300); // 模擬請求間隔} catch (InterruptedException e) {e.printStackTrace();}}}
}


對比總結

方式

適用場景

固定窗口

簡單低頻場景

滑動窗口

需平滑限流的場景

漏桶算法

恒定速率處理(如流量整形)

令牌桶算法

允許突發的場景(如秒殺)

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

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

相關文章

《TCP/IP網絡編程》學習筆記 | Chapter 18:多線程服務器端的實現

《TCP/IP網絡編程》學習筆記 | Chapter 18&#xff1a;多線程服務器端的實現 《TCP/IP網絡編程》學習筆記 | Chapter 18&#xff1a;多線程服務器端的實現線程的概念引入線程的背景線程與進程的區別 線程創建與運行pthread_createpthread_join可在臨界區內調用的函數工作&#…

創新實踐分享:基于邊緣智能+扣子的智能取物機器人解決方案

在 2024 年全國大學生物聯網設計競賽中&#xff0c;火山引擎作為支持企業&#xff0c;不僅參與了賽道的命題設計&#xff0c;還為參賽隊伍提供了相關的硬件和軟件支持。以邊緣智能和扣子的聯合應用為核心&#xff0c;參賽者們在這場競賽中展現出了卓越的創新性和實用性&#xf…

QT:動態屬性和對象樹

動態對象 1.添加Q_PROPERTY對象 #ifndef MYPROPERTYCLASS_H #define MYPROPERTYCLASS_H#include <QObject>class MyPropertyClass : public QObject {Q_OBJECTQ_PROPERTY(QString mask READ mask WRITE setMask NOTIFY maskChanged) public:explicit MyPropertyClass(Q…

MobileNet家族:從v1到v4的架構演進與發展歷程

MobileNet 是一個專為移動設備和嵌入式系統設計的輕量化卷積神經網絡&#xff08;CNN&#xff09;家族&#xff0c;旨在在資源受限的環境中實現高效的圖像分類、對象檢測和語義分割等任務。自 2017 年首次推出以來&#xff0c;MobileNet 經歷了從 v1 到 v4 的多次迭代&#xff…

在 Windows 上使用 choco 安裝 mkcert 并配置 Vue 運行HTTPS

解決在Windows上使用Vue本地運行HTTPS的問題,vue-cli或vite都可以使用 步驟 1&#xff1a;確認 Chocolatey 是否已安裝 1. 檢查 choco 命令是否可用 打開 PowerShell&#xff08;管理員權限&#xff09;&#xff0c;輸入&#xff1a; choco -v如果顯示版本號&#xff08;如…

【PHP】新版本特性記錄(持續更新)

文章目錄 前言PHP 7.01&#xff09;NULL合并運算符&#xff1a;??2&#xff09;參數、返回值支持類型聲明3&#xff09;太空船操作符&#xff1a;<>4&#xff09;通過 define 定義常量數組5&#xff09;匿名類實例化6&#xff09;字符串里使用\u轉義unicode codepoint …

【記】如何理解kotlin中的委托屬性?

1. 什么是委托屬性&#xff1f; 委托屬性的核心思想是&#xff1a; 你可以將一個屬性的 getter 和 setter 的邏輯交給一個外部對象&#xff08;稱為委托對象&#xff09;來處理。這個外部對象負責存儲屬性的值&#xff0c;并提供自定義的 get 和 set 行為。 通過委托屬性&am…

使用自動導入后,eslint報錯 eslint9

前提&#xff1a;使用pnpm create vuelatest創建vue應用&#xff0c;并且在創建項目時就勾選eslint和prettier&#xff0c;不然有些配置還需要手動配&#xff0c;比如解決eslint和prettier的沖突問題 1. 解決使用自動導入后Eslint報錯問題 配置vite.config.ts // 自動導入api…

springboot EasyExcel 實現導入導出

1. 添加依賴 確保 Maven 依賴中包含 EasyExcel 3.0.5&#xff1a; <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.5</version></dependency><!-- excel工具 --><dep…

實現懸浮按鈕拖動,兼容h5和微信小程序

h5用js寫&#xff0c;微信小程序用 代碼里面沒有完全實現吸附邊緣的功能&#xff0c;需要吸附邊緣的話還得自己再完善下&#xff08;h5的吸附邊緣是可以的&#xff0c;小程序的還有點問題&#xff09; 主要功能是&#xff1a;圖片上寫文字的懸浮按鈕&#xff0c;文字使用的是…

2、操作系統之軟件基礎

一、硬件支持系統 &#xff0c;系統管理硬件 操作系統核心功能可以分為&#xff1a; 守護者&#xff1a;對硬件和軟件資源的管理協調者&#xff1a;通過機制&#xff0c;將各種各樣的硬件資源適配給軟件使用。 所以為了更好的管理硬件&#xff0c;操作系統引進了軟件。其中3大…

17 | 實現簡潔架構的 Biz 層

提示&#xff1a; 所有體系課見專欄&#xff1a;Go 項目開發極速入門實戰課&#xff1b;歡迎加入 云原生 AI 實戰 星球&#xff0c;12 高質量體系課、20 高質量實戰項目助你在 AI 時代建立技術競爭力&#xff08;聚焦于 Go、云原生、AI Infra&#xff09;&#xff1b;本節課最終…

idea更新git代碼報錯No Git Roots

idea更新git代碼報錯&#xff1a; No Git Roots None of configured Git roots are under Git. The configured directory must have ".git directory in it.但是本地項目里是存在.git文件的&#xff0c;就是突然間不能更新代碼了 然后嘗試重新拉新項目代碼提示: Git i…

Webpack 知識點整理

? 1. 對 webpack 的理解&#xff1f;解決了什么問題&#xff1f; Webpack 是前端工程化領域的核心工具&#xff0c;其核心定位是模塊打包器&#xff08;Module Bundler&#xff09;&#xff0c;通過將各類資源&#xff08;JS、CSS、圖片等&#xff09;視為模塊并進行智能整合…

[Hello-CTF]RCE-Labs超詳細WP-Level13Level14(PHP下的0/1構造RCE命令簡單的字數限制RCE)

Level 13 源碼分析 這題又回到了 PHP重點關注preg_match("/[A-Za-z0-9\"%*,-.\/:;>?[\]^|]/", $cmd)禁用了所有數字, 并且回到了 PHP, 沒辦法用上一關的方法進行繞過但是比起上一關, 給我們少繞過了 &, ~, _似乎有其他方法 解題分析 利用 $(()) 和 …

Qt 控件概述 QWdiget 1.1

目錄 qrc機制 qrc使用 1.在項目中創建一個 qrc 文件 2.將圖片導入到qrc文件中 windowOpacity&#xff1a; cursor 光標 cursor類型 自定義Cursor font tooltip focusPolicy styleSheet qrc機制 之前提到使用相對路徑的方法來存放資源&#xff0c;還有一種更好的方式…

【eNSP實戰】將路由器配置為DHCP服務器

拓圖 要求&#xff1a; 為 office100 和 office200 分別配置地址池 AR1接口配置 interface GigabitEthernet0/0/0ip address 192.168.100.1 255.255.255.0 # interface GigabitEthernet0/0/1ip address 192.168.200.1 255.255.255.0 AR1路由器上創建office100地址池 [AR1…

數據結構——順序表seqlist

前言&#xff1a;大家好&#x1f60d;&#xff0c;本文主要介紹了數據結構——順序表部分的內容 目錄 一、線性表的定義 二、線性表的基本操作 三.順序表 1.定義 2. 存儲結構 3. 特點 四 順序表操作 4.1初始化 4.2 插入 4.2.1頭插 4.2.2 尾插 4.2.3 按位置插 4.3 …

OSPF | LSDB 鏈路狀態數據庫 / SPF 算法 / 實驗

注&#xff1a;本文為 “OSPF | LSDB / SPF ” 相關文章合輯。 LSDB 和 SPF 算法 瀟湘浪子的蹋馬骨湯 發布 2019-02-15 23:58:46 1. 鏈路狀態數據庫 (LSDB) 鏈路狀態協議除了執行洪泛擴散鏈路狀態通告&#xff08;LSA&#xff09;以及發現鄰居等任務外&#xff0c;其第三個任…

前端---CSS(前端三劍客)

1.基本語法規范 選擇器 {?條/N條聲明} ? 選擇器決定針對誰修改 (找誰) ? 聲明決定修改啥. (?啥) ? 聲明的屬性是鍵值對. 使? ; 區分鍵值對, 使? : 區分鍵和值 比如&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta…