一、什麼是限流
在計算機系統中,流量控制(Flow Control)是指通過對數據的傳輸速率、數據量或者數據傳輸速度等進行控制和限制,以避免數據因為太快、太多而導致系統癱瘓、崩潰等問題。限流是流量控制的一種常見方法,常用於保護系統免於被大量的請求耗盡資源或者被攻擊。
二、為什麼需要限流
隨着互聯網行業的迅速發展,數據的存取和傳輸量越來越大,具有“爆炸性增長”的特點。針對這種情況,限流的存在是必不可少的,特別是在面對一些高並發場景時,如果沒有限流可能導致系統崩潰甚至服務宕機。
三、常見的限流算法
1. 固定窗口算法
固定窗口算法是一種最簡單的計數器算法,即某個時間窗口內只能處理一定數量的請求,如果到達這個計數器的上限,其他請求就被拒絕,這種算法可以使用計數器實現,當計數器的數量達到限制時,就拒絕其他請求。
例如,設置一個固定的時間窗口(比如1秒鐘),對於這個時間窗口內,最多只能處理n個請求(比如100個),超過這個數量的請求就直接被拒絕,不會被處理。
/*redis實現固定窗口算法*/ def is_action_allowed(action_key, action_count): with redis.client.pipeline() as pipe: pipe.set(action_key, 0, ex=time_window) /*先Set Zero*/ pipe.incr(action_key) /*Incr (被調用就加1)*/ pipe.execute() /*執行*/ return pipe.get(action_key) < action_count /*UpLimiter*/
2. 滑動窗口算法
固定窗口算法有一個明顯的缺陷,如果在某個短時間內有大量請求到達,就會使得固定時間窗口內的總請求數過多,而滑動窗口算法就是為了解決這個缺陷而出現的。滑動窗口算法將時間窗口分成多個小窗口,每個小窗口的數量上限都和整個時間窗口的數量相等。
例如,將時間窗口分成10個小窗口,每個小窗口的上限為10個請求,那麼整個時間窗口內的請求上限就為100個請求。當有新來的請求時,就從右側進入並且移動,超出限制就直接被拒絕。
/*redis實現滑動窗口算法*/ def is_action_allowed(action_key, action_count): with redis.client.pipeline() as pipe: timestamp = time.time() // time_window /*當前unix時間戳*/ pipe.zadd(action_key, {timestamp: timestamp}) pipe.zremrangebyscore(action_key, 0, timestamp - time_window) /*返回Interval */ pipe.zcard(action_key) pipe.execute() return count <= action_count
3. 令牌桶算法
令牌桶算法是一種比較常用的限流算法,它維護一個固定大小的令牌桶,以及一個容量固定的等待隊列,當有請求來臨時,如果桶內有足夠的令牌(也就是令牌桶處於“非空”狀態),就將其取出並處理;如果桶內沒有令牌(也就是令牌桶處於“空”狀態),則將請求加入到等待隊列中,等待下一次有令牌時再進行處理。
例如,設置令牌桶的容量為100,而這個令牌桶每秒鐘產生100個令牌。每來一個請求,就先去判斷當前令牌桶的令牌數量,如果桶中的令牌數量不夠,則拒絕這個請求,否則就將令牌數減1。
/*redis實現令牌桶算法*/ def is_action_allowed(token_bucket_key, capacity, tokens_per_second): with redis.client.pipeline() as pipe: pipe.setnx(token_bucket_key, capacity) /*setmax*/ /*累加時,要求原始值在新值之前*/ last_tokens, capacity = pipe.multi() \ .get(token_bucket_key) \ .strtol(tokens_per_second) \ .execute() if last_tokens + tokens_per_second < capacity: pipe.set(token_bucket_key, last_tokens + tokens_per_second) else: return False return True
四、如何使用Redis實現限流
Redis是一款高性能內存數據庫,而且具有豐富的功能,我們可使用Redis作為限流工具,來實現高並發下的限流。
1. 通過Redis的單線程模型進行限流
Redis的單線程模型設計並發的特性非常適合做限流,可以利用Redis的原子性和分布式特性使用Lua腳本快速實現限流邏輯。
def is_action_allowed(key, limit, expire=60): with redis.client.pipeline() as pipe: key = key + str(int(time.time()/expire)) pipe.incr(key) pipe.expire(key, expire) pipe.execute() total = pipe.get(key) return total <= limit
2. 通過Redis+Lua實現令牌桶算法
使用Lua腳本實現原子性的操作,並且減少了網絡傳輸的時間延遲,也提供了更為方便的桶容量可控性和更細緻的訪問轉移控制。
redis.call()
五、結論
本文主要介紹了Redis在高並發場景下限流的常見算法以及如何使用Redis來實現限流功能。限流的目的是保護系統免於被大量請求耗盡資源或者被攻擊,而Redis的特點適合限流這種高並發業務場景。希望本文能夠幫助到大家,也希望讀者在實際開發中選擇適合自己業務場景的限流算法。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/272124.html