基于Redis實現令牌桶算法
- 令牌桶算法
- 算法流程圖
- 優點
- 缺點
- 實現
- 其它限流算法
令牌桶算法
令牌桶是一種用于分組交換和電信網絡的算法。它可用于檢查數據包形式的數據傳輸是否符合定義的帶寬和突發性限制(流量不均勻或變化的衡量標準)。它還可以用作調度算法來確定符合帶寬和突發性限制設置的傳輸時序。
算法流程圖
如圖所示,令牌桶算法可以描述為:
- 令牌桶初始大小和容量為X
- 以一定速率Y向令牌桶中添加令牌,如果令牌桶滿了,忽略多余令牌
- 每次請求令牌桶拿Z個令牌,如果當前令牌桶不足,則拒絕當前請求
優點
- 平滑流量:
令牌桶可以通過控制令牌放入速率(Y)平滑流量,防止過高負載導致系統崩潰 - 可以處理突發流量
令牌桶可以積累一定量的令牌以應對突發流量 - 靈活
通過調整令牌桶的容量和令牌放入速率,可以靈活地控制突發容量和請求的平均處理速率。這種靈活性使得令牌桶算法能夠適應不同應用場景。
缺點
- 資源占用
令牌桶算法需要維護當前令牌數和上次放入時間, 這會耗費較高的計算資源。 - 參數調整復雜
令牌桶算法需要調整容量(X)、速率(Y)和每次請求令牌數量(Z),不合適的參數有可能導致請求分配不公平乃至饑餓請求(長時間無法獲取到令牌的請求)。 - 依賴系統時間
令牌桶算法在計算令牌數量時依賴于系統時間。如果系統時間發生異常(如時間回撥),則可能會導致算法失效或產生不可預測的結果。
實現
本文是使用Redis的Lua腳本來實現的
--- 實現令牌桶算法
-- 以一定的頻率向令牌桶中放入令牌, 其它人使用時獲取令牌, 如果能夠獲取成功
local expire_time = 3600 -- 1個小時失效
local bucket_size = 20 -- 一個桶最多有20張令牌
local bucket_speed = 3.5 -- 每秒增加令牌的個數
local requests_token = 1 -- 請求一次耗費的令牌數local time = redis.call('TIME')
local current_time = time[1] * 1000 + time[2] / 1000 -- TIME返回的是 秒,納秒. 這里轉換成微秒
local token = KEYS[1]-- 獲取當前剩余令牌數
local bucket = redis.call('HGETALL', token)
local ret = 0
if table.maxn(bucket) == 0 then-- 令牌桶已失效或者還沒有設置redis.call('HSET', token, 'lastRefillTime', current_time)redis.call('HSET', token, 'tokensRemain', bucket_size - requests_token)redis.call('PEXPIRE', token, expire_time * 1500)ret = 1
else-- 上次填充時間local lastRefillTime = tonumber(bucket[2])-- 剩余令牌數local tokensRemain = tonumber(bucket[4])-- 距離上次調用時間local cost_time = current_time - lastRefillTimeif cost_time < 0 then-- 發生了時間回撥, 令牌不再增加current_time = lastRefillTimeif tokensRemain >= requests_token thentokensRemain = tokensRemain - requests_tokenret = 1endelseif tokensRemain >= 1 or cost_time * bucket_speed / 1000 >= requests_token thentokensRemain = math.min(tokensRemain + (cost_time * bucket_speed / 1000) - requests_token, bucket_size - requests_token)ret = 1elsetokensRemain = tokensRemain + (cost_time * bucket_speed / 1000)ret = 0endredis.call('HSET', token, 'lastRefillTime', current_time)redis.call('HSET', token, 'tokensRemain', tokensRemain)redis.call('PEXPIRE', token, expire_time * 1500)
end
return ret
其它限流算法
- 計數器算法
- 滑動窗口算法
- 漏桶算法