一文帶你全面瞭解限流算法

雲棲號資訊:【點擊查看更多行業資訊
在這裏您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!


大多數情況下,我們不需要自己實現一個限流系統,但限流在實際應用中是一個非常微妙、有很多細節的系統保護手段,尤其是在高流量時,瞭解你所使用的限流系統的限流算法,將能很好地幫助你充分利用該限流系統達到自己的商業需求和目的,並規避一些使用限流系統可能帶來的大大小小的問題。

令牌桶算法

令牌桶(token bucket)算法,指的是設計一個容器(即“桶”),由某個組件持續運行往該容器中添加令牌(token),令牌可以是簡單的數字、字符或組合,也可以僅僅是一個計數,然後每個請求進入系統時,需要從桶中領取一個令牌,所有請求都必須有令牌才能進入後端系統。當令牌桶空時,拒絕請求;當令牌桶滿時,不再往其中添加新的令牌。

令牌桶算法的架構如圖1所示:

1

圖1 令牌桶算法

令牌桶算法的實現邏輯如下:

首先會有一個定義的時間窗口的訪問次數閾值,例如每天1000人,每秒5個請求之類,限流系統一般最小粒度是秒,再小就會因爲實現和性能的原因而變得不準確或不穩定,假設是T秒內允許N個請求,那麼令牌桶算法則會使令牌添加組件每T秒往令牌桶中添加N個令牌。

其次,令牌桶需要有一個最大值M,當令牌添加組件檢測到令牌桶中已經有M個令牌時,剩餘的令牌會被丟棄。反映到限流系統中,可以認爲是當前系統允許的瞬時最大流量,但不是持續最大流量。例如令牌桶中的令牌最大數量是100個,每秒鐘會往其中添加10個新令牌,當令牌滿的時候,突然出現100 TPS的流量,這時候是可以承受的,但是假如連續兩秒的100 TPS流量就不行,因爲令牌添加速度是一秒10個,添加速度跟不上使用速度。

因此,凡是使用令牌桶算法的限流系統,我們都會注意到它在配置時要求兩個參數:

  • 平均閾值(rate或average)
  • 高峯閾值(burst或peak)

通過筆者的介紹,讀者應該意識到,令牌桶算法的高峯閾值是有特指的,並不是指當前限流系統允許的最高流量。因爲這一描述可能會使人認爲只要低於該閾值的流量都可以,但事實上不是這樣,因爲只要高於添加速度的流量持續一段時間都會出現問題。

反過來說,令牌桶算法的限流系統不容易計算出它支持的最高流量,因爲它能實時支持的最高流量取決於那整個時間段內的流量變化情況即令牌存量,而不是僅僅取決於一個瞬時的令牌量。

最後,當有組件請求令牌的時候,令牌桶會隨機挑選一個令牌分發出去,然後將該令牌從桶中移除。注意,此時令牌桶不再做別的操作,令牌桶永遠不會主動要求令牌添加組件補充新的令牌。

令牌桶算法有一個同一思想、方向相反的變種,被稱爲漏桶(leaky bucket)算法,它是令牌桶的一種改進,在商業應用中非常廣泛。

漏桶算法的基本思想,是將請求看作水流,用一個底下有洞的桶盛裝,底下的洞漏出水的速率是恆定的,所有請求進入系統的時候都會先進入這個桶,並慢慢由桶流出交給後臺服務。桶有一個固定大小,當水流量超過這個大小的時候,多餘的請求都會被丟棄。

漏桶算法的架構如圖2所示:

2

圖2 漏桶算法

漏桶算法的實現邏輯如下:

  • 首先會有一個容器存放請求,該容器有一個固定大小M,所有請求都會被先存放到該容器中。
  • 該容器會有一個轉發邏輯,該轉發以每T秒N個請求的速率循環發生。
  • 當容器中請求數已經達到M個時,拒絕所有新的請求。

因此同樣地,漏桶算法的配置也需要兩個值:平均值(rate)和峯值(burst)。只是平均值這時候是用來表示漏出的請求數量,峯值則是表示桶中可以存放的請求數量。

注意:漏桶算法和緩衝的限流思想不是一回事!

同樣是將請求放在一個容器中,漏桶算法和緩衝不是一個用途,切不可搞混,它們的區別如下:

  • 漏桶算法中,存在桶中的請求會以恆定的速率被漏給後端業務服務器,而緩衝思想中,放在緩衝區域的請求只有等到後端服務器空閒下來了,纔會被髮出去。
  • 漏桶算法中,存在桶中的請求是原本就應該被系統處理的,是系統對外界宣稱的預期,不應該被丟失,而緩衝思想中放在緩衝區域的請求僅僅是對意外狀況的儘量優化,並沒有任何強制要求這些請求可以被處理。

漏桶算法和令牌桶算法在思想上非常接近,而實現方向恰好相反,它們有如下的相同和不同之處:

  • 令牌桶算法以固定速率補充可以轉發的請求數量(令牌),而漏桶算法以固定速率轉發請求;
  • 令牌桶算法限制數量的是預算數,漏桶算法限制數量的是實際請求數;
  • 令牌桶算法在有爆發式增長的流量時可以一定程度上接受,漏桶算法也是,但當流量爆發時,令牌桶算法會使業務服務器直接承擔這種流量,而漏桶算法的業務服務器感受到的是一樣的速率變化。

因此,通過以上比較,我們會發現漏桶算法略優於令牌桶算法,因爲漏桶算法對流量控制更平滑,而令牌桶算法在設置的數值範圍內,會將流量波動忠實地轉嫁到業務服務器頭上。

漏桶算法在Nginx和分佈式的限流系統例如Redis的限流功能中都有廣泛應用,是目前業界最流行的算法之一。

時間窗口算法

時間窗口算法是比較簡單、基礎的限流算法,由於它比較粗略,不適合大型、流量波動大或者有更精細的流量控制需求的網站。

時間窗口算法根據確定時間窗口的方式,可以分爲兩種:

  • 固定時間窗口算法
  • 滑動時間窗口算法

固定時間窗口算法最簡單,相信如果讓初次接觸限流理念的讀者去快速設計實現一個限流系統的話,也可以很快想到這種算法。這種算法即固定一個時間段內限定一個請求閾值,沒有達到則讓請求通過,達到數量閾值了就拒絕請求。步驟如下:

  • 先確定一個起始時間點,一般就是系統啓動的時間。
  • 從起始時間點開始,根據我們的需求,設置一個最大值M,開始接受請求並從0開始爲請求計數。
  • 在時間段T內,請求計數超過M時,拒絕所有剩下的請求。
  • 超過時間段T後,重置計數。

固定時間窗口算法的思路固然簡單,但是它的邏輯是有問題的,它不適合流量波動大和有精細控制流量需求的服務。讓我們看以下例子:

假設我們的時間段T是1秒,請求最大值是10,在第一秒內,請求數量分佈是第500毫秒時有1個請求,第800毫秒時有9個請求,如圖3所示:

3

圖3 固定時間窗口限流第一秒的請求分佈

這是對於第一秒而言,這個請求分佈是合理的。

此時第二秒的第200毫秒(即兩秒中的第1200毫秒)內,又來了10個請求,如圖4所示:

4

圖4 固定時間窗口限流第二秒的請求分佈

單獨看第二秒依然是合理的,但是兩個時間段連在一起的時候,就出現了問題,如圖5所示:

5

圖5 固定時間窗口限流的頭兩秒請求分佈

從500毫秒到1200毫秒,短短700毫秒的時間內後端服務器就接收了20個請求,這顯然違背了一開始我們希望1秒最多10個的初衷。這種遠遠大於預期流量的流量加到後端服務器頭上,是會造成不可預料的後果的。因此,人們改進了固定窗口的算法,將其改爲檢查任何一個時間段都不超過請求數量閾值的時間窗口算法:滑動時間窗口算法。

滑動時間窗口算法要求當請求進入系統時,回溯過去的時間段T,找到其中的請求數量,然後決定是否接受當前請求,因此,滑動時間窗口算法需要記錄時間段T內請求到達的時間點,邏輯如圖6所示:

6

圖6 滑動時間窗口限流系統的邏輯

解釋如下:

1、確定一個起始時間點,一般就是系統啓動的時間,並記錄該點爲時間窗口的開始點。然後創建一個空的列表作爲時間窗口內請求進入的時間戳記錄。

2、當請求到來時,使用當前時間戳比較它是否在時間窗口起始點加上T時間段(從開始點到開始點+T就是時間窗口)內。

  • 如果在,則查看當前時間窗口內記錄的所有請求的數量:

如果超過,則拒絕請求。

如果沒有,則將該請求加入到時間戳記錄中,並將請求交給後端業務服務器。

  • 如果不在,則查看時間戳記錄,將時間戳最久遠的記錄刪除,然後將時間窗口的開始點更新爲第二久遠的記錄時間,然後回到步驟2,再次檢查時間戳是否在時間窗口內。

滑動時間窗口儘管有所改進,但依然不能很好應對某個時間段內突發大量請求,而令牌桶和漏桶算法就由於允許指定平均請求率和最大瞬時請求率,它比時間窗口算法控制更精確。

時間窗口算法可以通過多時間窗口來改進。例如,可以設置一個1秒10 TPS的時間窗口限流和一個500毫秒5 TPS的時間窗口限流,二者同時運行,如此就可以保證更精確的限流控制。

隊列法

隊列法與漏桶算法很類似,都是將請求放入到一個區域,然後業務服務器從中提取請求,但是隊列法採用的是完全獨立的外部系統,而不是依附於限流系統。隊列法的架構如圖7所示:

7

圖7 使用隊列限流的架構

與漏桶算法相比,隊列法的優勢如下:

  • 由業務邏輯層決定請求收取的速度。限流系統即隊列不需要再關注流量的設置(例如T是多少,N是多少,M又是多少等等),只需要專注保留髮送的請求,而業務服務器由於完全掌控消息的拉取,可以根據自身條件決定請求獲取的速度,更加自由。
  • 完全將業務邏輯層保護起來,並且可以增加服務去消費這些請求。這一手段將業務服務器完全隱藏在了客戶端後面,由隊列去承擔所有流量,也可以更好地保護自身不受到惡意流量的攻擊。
  • 隊列可以使用更健壯、更成熟的服務,這些服務比限流系統複雜,但能夠承受大得多的流量。例如,業務服務器使用的是像阿里雲或者AWS這樣的消息隊列的話,業務服務器就不用擔心擴容的問題了,只要請求對實時性的要求不高。業務服務器由於使用了雲服務,隊列一端的擴容不用擔心,而由於消息是自由決定拉取頻率和處理速度,自身的擴容壓力也就不那麼大了。

但隊列法最大的缺陷,就是服務器不能直接與客戶端溝通,因此只適用於客戶端令業務服務器執行任務且不要求響應的用例,所有客戶端需要有實質響應的服務都不能使用。例如,業務服務器提供的服務是消息發送服務,那麼這種模式就可以的,但如果客戶端是請求某些用戶信息,那這種方式就完全不可行了。

【雲棲號在線課堂】每天都有產品技術專家分享!
課程地址:https://yqh.aliyun.com/live

立即加入社羣,與專家面對面,及時瞭解課程最新動態!
【雲棲號在線課堂 社羣】https://c.tb.cn/F3.Z8gvnK

原文發佈時間:2020-07-17
本文作者:Andy_Lee
本文來自:“dockone”,瞭解相關信息可以關注“dockone”

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章