通常限流器有五種算法,分別是:令牌桶,漏斗桶,固定窗口,滑動日誌(指的實際上是廣義上的滑動窗口),滑動窗口(這裏指的是滑動日誌+固定窗口結合的一種算法)。redis
令牌桶算法用來控制一段時間內發送到網絡上的數據的數目,並容許突發數據的發送。算法
算法大概是: 假設容許的請求速率爲r
次每秒,那麼每過1/r
秒就會向桶裏面添加一個令牌。桶的最大大小是b
。當一個大小爲n
的請求到來時,檢查桶內令牌數是否足夠,若是足夠,令牌數減小n
,請求經過。不夠的話就會觸發拒絕策略。緩存
令牌桶有一個固定大小,假設每個請求也有一個大小,當要檢查請求是否符合定義的限制時,會檢查桶,以肯定它當時是否包含足夠的令牌。若是有,那麼會移除掉這些令牌,請求經過。不然,會採起其餘操做,通常是拒絕。令牌桶中的令牌會以必定速率恢復,這個速率就是容許請求的速率(固然,根據大小的配置,可能實際會超過這個速率,可是隨着令牌桶的消耗會被調整回這個恢復速率)。網絡
若是令牌不被消耗,或者被消耗的速度小於產生的速度,令牌就會不斷地增多,直到把桶填滿。能夠看出,令牌桶在保持總體上的請求速率的同時,容許某種程度的突發傳輸。架構
分佈式環境下的令牌桶的實現須要考慮以下幾個問題:併發
r
不斷地往裏面添加令牌,那麼如何在分佈式的環境下保證有且只有一個這樣的進程,這個進程掛了怎麼辦?對於存放每一個經過的請求到來的時間戳的這種實現方式實現,那麼怎麼控制記錄請求的個數,確定不能每一個都記錄,而且每次怎麼經過目前的請求以及時間戳來判斷剩餘令牌數量漏斗桶控制請求必須在最大某個速率被消費,就像一個漏斗同樣,入水量可大可小,可是最大速率只能到某一量值,不會像令牌桶同樣,會有小的尖峯。分佈式
算法大概是: 主要實現方式是經過一個 FIFO (First in first out)的隊列實現,這個隊列是一個有界隊列,大小爲b
,若是請求堆積滿了隊列,就會觸發丟棄策略。假設容許的請求速率爲r
次每秒,那麼這個隊列中的請求,就會以這個速率進行消費。ide
分佈式環境下的漏桶的實現須要考慮以下幾個問題:高併發
1. 漏桶的隊列,怎麼存放?這個隊列須要存放每一個經過的請求以及對應的消費的時間戳,保證消費的平穩。同時,這個隊列最好是無鎖隊列,由於會有分佈式鎖徵用。而且,這個隊列大小應該設置爲b
,並每次有請求到來時,放入隊列的同時清理隊列。
2. 消費如何實現?也就是存入隊列的請求,如何消費呢?能夠請求到來時,經過隊列中的請求來判斷當前這個請求的執行時間應該是多久之後,以後入隊列,延遲這麼久再執行這個請求。也能夠利用自己帶延遲時間實現的隊列來實現。優化
固定時間窗口比較簡單,就是將時間切分紅若干個時間片,每一個時間片內固定處理若干個請求。這種實現不是很是嚴謹,可是因爲實現簡單,適用於一些要求不嚴格的場景。
算法大概是: 假設n
秒內最多處理b
個請求,那麼每隔n
秒將計數器重置爲b
。請求到來時,若是計數器值足夠,則扣除並請求經過,不夠則觸發拒絕策略。
固定時間窗口是最容易實現的算法,可是也是有明顯的缺陷:那就是在不少狀況下,尤爲是請求限流後拒絕策略爲排隊的狀況下,請求都在時間窗口的開頭被迅速消耗,剩下的時間不處理任何請求,這是不太可取的。而且,在一些極限狀況下,實際上的流量速度可能達到限流的 2 倍。例如限制 1 秒內最多 100 個請求。假設 0.99 秒的時候 100 個請求到了,以後 1.01 秒的時候又有 100 個請求到了,這樣的話其實在 0.99 秒 ~ 1.01 秒這一段時間內有 200 個請求,並非嚴格意義上的每一秒都只處理 100 個請求。爲了能實現嚴格意義上的請求限流,則有了後面兩種算法。
滑動日誌根據緩存以前接受請求對應的時間戳,與當前請求的時間戳進行計算,控制速率。這樣能夠嚴格限制請求速率。通常的網上提到的滑動窗口算法也指的是這裏的滑動日誌(Sliding Log)算法,可是咱們這裏的滑動窗口是另外一種優化的算法,待會會提到。
算法大概是: 假設n
秒內最多處理b
個請求。那麼會最多緩存 b
個經過的請求與對應的時間戳,假設這個緩存集合爲B
。每當有請求到來時,從B
中刪除掉n
秒前的全部請求,查看集合是否滿了,若是沒滿,則經過請求,並放入集合,若是滿了就觸發拒絕策略。
分佈式環境下的滑動日誌的實現須要考慮以下幾個問題:
n
秒前的全部請求的這個操做,須要速度很是快。若是你的緩存集合實現對於按照時間戳刪除這個操做比較慢,能夠緩存多一點請求,定時清理刪除n
秒前的全部請求而不是每次請求到來都刪除。請求到來的時候,查看b
個以前的請求是否存在而且時間差小於n
秒,存在而且小於表明應該觸發限流策略。前面的滑動日誌,咱們提到了一個問題 - 要緩存的請求可能會不少。也許在咱們的架構內不能使用一個恰當的緩存來實現,咱們能夠經過滑動窗口這個方法來減小要存儲的請求數量,並減小集合大小減小同一個集合上面的併發。
算法大概是: 假設n
秒內最多處理b
個請求。咱們能夠將n
秒切分紅每一個大小爲m
毫秒得時間片,只有最新的時間片內緩存請求和時間戳,以前的時間片內只保留一個請求量的數字。這樣能夠大大優化存儲,小幅度增長計算量。對於臨界條件,就是以前已經有了n/m
個時間片,計算n
秒內請求量時能夠計算當前時間片內通過時間的百分比,假設是 25%,那麼就取開頭的第一個時間片的請求量的 75% 進行計算。
每日一刷,輕鬆提高技術,斬獲各類offer: