限流(Rate Limiting)是保障系統穩定性和服務質量的關鍵機制,尤其在高并發、突發流量、攻擊防護等場景中至關重要。
本文將詳細介紹四種主流限流算法:
固定窗口(Fixed Window)
滑動窗口(Sliding Window)
令牌桶(Token Bucket)
漏桶算法(Leaky Bucket)
我們將從原理、實現方式、優缺點和適用場景四個維度進行深入分析和對比。
一、固定窗口(Fixed Window)
原理:將時間劃分為固定長度窗口,統計每個窗口內的請求數,超過上限則拒絕。
實現方式(常用 Redis INCR + EXPIRE):
key = f"req:{user_id}:{current_minute}"
count = redis.incr(key)
if count == 1:redis.expire(key, 60)
if count > threshold:reject()
優點:
實現簡單,性能高。
Redis 支持天然適配。
缺點:
臨界突刺問題:在兩個窗口交界點,可能放行兩倍請求。
適用場景:
精度要求不高的接口。
管控類后臺系統。
二、滑動窗口(Sliding Window)
1. 滑動日志(Sliding Log)
原理:記錄每次請求時間戳,實時清理超出時間窗口的舊請求,判斷窗口內數量。
優點:
流量限制更精確。
避免突刺。
缺點:
需要維護時間戳列表,內存消耗較大。
2. 滑動窗口計數器(Sliding Window Counter)
原理:將一個窗口拆分為多個小窗口,每個子窗口統計請求數,根據時間加權合并。
優點:
性能和精度之間較好平衡。
適用場景:
高并發、登錄、敏感操作等流控要求較高的業務。
三、令牌桶算法(Token Bucket)
原理:
系統以固定速率放入“令牌”到桶中;
每次請求需要“取一個令牌”才能通過;
桶容量有限,超過上限的令牌被丟棄;
若桶為空,請求被限流(或排隊等)。
示意圖:
+----------+
| 令牌桶 |
| |<-- 固定速率生成令牌
| [ ] |
+----------+↓請求來取令牌 -> 成功 or 被限流
優點:
支持突發流量(令牌可積累)。
靈活控制平均速率與突發能力。
Guava / Nginx 都有成熟實現。
缺點:
實現較復雜。
依賴精準時間調度。
適用場景:
接口限頻,突發高并發業務。
LLM API 限流 / OpenAI、Stripe 等系統。
四、漏桶算法(Leaky Bucket)
原理:
所有請求先進入一個桶中;
桶以固定速率“漏水”處理請求;
桶滿時,新請求要么被丟棄,要么排隊等待。
示意圖:
請求 → 桶(隊列) → 以恒定速率處理(漏水)↑桶滿則拒絕或排隊
實現方式:
可以用一個隊列存儲請求;
后臺定期以固定速率出隊并處理請求。
優點:
平滑處理請求流量,保持處理速率穩定。
防止服務被瞬時流量壓垮。
缺點:
不支持突發流量(桶中積壓,排隊延遲)。
實現復雜度略高于令牌桶。
適用場景:
網關、負載均衡器的請求調度。
強調處理速率恒定的后臺任務系統。
五、算法對比總結
特性 | 固定窗口 | 滑動窗口 | 令牌桶 | 漏桶 |
---|---|---|---|---|
實現復雜度 | ?(簡單) | ??(中等) | ???(中高) | ??(中等) |
限流精度 | 低 | 高 | 中等 | 高 |
是否支持突發請求 | 否 | 否 | ? | ? |
是否平滑 | ? | ? | ? | ? |
是否常用 | ?(Redis) | ?(Sentinel) | ?(Guava、Nginx) | ?(調度系統) |
六、實戰建議
簡單限流場景(如后臺管理):使用 固定窗口 + Redis 即可。
高并發接口限頻:推薦 滑動窗口 或 令牌桶,后者更適合突發請求。
需要平滑處理隊列任務:選擇 漏桶算法。
七、結語
限流并不是一刀切的“阻止請求”,而是為系統爭取喘息空間。不同的限流算法背后體現的是不同的設計哲學與業務權衡。
選對限流算法,既可以保護系統,又能優化用戶體驗。