請你講講分布式系統中的限流器一般如何實現?

2021-10-18 13:46:49 字數 3906 閱讀 3011

一般限流器有五種演算法,分別是:令牌桶,漏斗桶,固定視窗,滑動日誌(指的其實是廣義上的滑動視窗),滑動視窗(這裡指的是滑動日誌+固定視窗結合的一種演算法)。

1. 令牌桶(token bucket)

令牌桶演算法用來控制一段時間內傳送到網路上的資料的數目,並允許突發資料的傳送。

演算法大概是:假設允許的請求速率為r次每秒,那麼每過1/r秒就會向桶裡面新增乙個令牌。桶的最大大小是b。當乙個大小為n的請求到來時,檢查桶內令牌數是否足夠,如果足夠,令牌數減少n,請求通過。不夠的話就會觸發拒絕策略。

令牌桶有乙個固定大小,假設每乙個請求也有乙個大小,當要檢查請求是否符合定義的限制時,會檢查桶,以確定它當時是否包含足夠的令牌。如果有,那麼會移除掉這些令牌,請求通過。否則,會採取其他操作,一般是拒絕。令牌桶中的令牌會以一定速率恢復,這個速率就是允許請求的速率(當然,根據大小的配置,可能實際會超過這個速率,但是隨著令牌桶的消耗會被調整回這個恢復速率)。

如果令牌不被消耗,或者被消耗的速度小於產生的速度,令牌就會不斷地增多,直到把桶填滿。可以看出,令牌桶在保持整體上的請求速率的同時,允許某種程度的突發傳輸。

分布式環境下的令牌桶的實現需要考慮如下幾個問題:

令牌桶當前大小究竟如何儲存?是只儲存乙個當前令牌桶的大小(例如通過 redis 的乙個鍵值對儲存),還是存放每個通過的請求到來的時間戳(例如通過 redis 的 zset 實現,zset 的大小就是桶的最大大小)?

令牌桶的令牌補充是由誰補充?對於儲存乙個當前令牌桶的大小的實現方式,需要乙個程序以速率r不斷地往裡面新增令牌,那麼如何在分布式的環境下保證有且只有乙個這樣的程序,這個程序掛了怎麼辦?對於存放每個通過的請求到來的時間戳的這種實現方式實現,那麼怎麼控制記錄請求的個數,肯定不能每個都記錄,並且每次怎麼通過目前的請求以及時間戳來判斷剩餘令牌數量

2. 漏斗桶(leaky bucket)

漏斗桶控制請求必須在最大某個速率被消費,就像乙個漏斗一樣,入水量可大可小,但是最大速率只能到某一量值,不會像令牌桶一樣,會有小的尖峰。

演算法大概是:主要實現方式是通過乙個 fifo (first in first out)的佇列實現,這個佇列是乙個有界佇列,大小為b,如果請求堆積滿了佇列,就會觸發丟棄策略。假設允許的請求速率為r次每秒,那麼這個佇列中的請求,就會以這個速率進行消費。

分布式環境下的漏桶的實現需要考慮如下幾個問題:

**1. 漏桶的佇列,怎麼存放?**這個佇列需要存放每個通過的請求以及對應的消費的時間戳,保證消費的平穩。同時,這個佇列最好是無鎖佇列,因為會有分布式鎖徵用。並且,這個佇列大小應該設定為b,並每次有請求到來時,放入佇列的同時清理佇列。

**2. 消費如何實現?**也就是存入佇列的請求,如何消費呢?可以請求到來時,通過佇列中的請求來判斷當前這個請求的執行時間應該是多久以後,之**佇列,延遲這麼久再執行這個請求。也可以利用本身帶延遲時間實現的佇列來實現。

3. 固定時間視窗(fixed window)

固定時間視窗比較簡單,就是將時間切分成若干個時間片,每個時間片內固定處理若干個請求。這種實現不是非常嚴謹,但是由於實現簡單,適用於一些要求不嚴格的場景。

演算法大概是:假設n秒內最多處理b個請求,那麼每隔n秒將計數器重置為b。請求到來時,如果計數器值足夠,則扣除並請求通過,不夠則觸發拒絕策略。

固定時間視窗是最容易實現的演算法,但是也是有明顯的缺陷:那就是在很多情況下,尤其是請求限流後拒絕策略為排隊的情況下,請求都在時間視窗的開頭被迅速消耗,剩下的時間不處理任何請求,這是不太可取的。並且,在一些極限情況下,實際上的流量速度可能達到限流的 2 倍。例如限制 1 秒內最多 100 個請求。假設 0.99 秒的時候 100 個請求到了,之後 1.01 秒的時候又有 100 個請求到了,這樣的話其實在 0.99 秒 ~ 1.01 秒這一段時間內有 200 個請求,並不是嚴格意義上的每一秒都只處理 100 個請求。為了能實現嚴格意義上的請求限流,則有了後面兩種演算法。

4. 滑動日誌(sliding log)

滑動日誌根據快取之前接受請求對應的時間戳,與當前請求的時間戳進行計算,控制速率。這樣可以嚴格限制請求速率。一般的網上提到的滑動視窗演算法也指的是這裡的滑動日誌(sliding log)演算法,但是我們這裡的滑動視窗是另一種優化的演算法,待會會提到

演算法大概是:假設n秒內最多處理b個請求。那麼會最多快取b個通過的請求與對應的時間戳,假設這個快取集合為b。每當有請求到來時,從b中刪除掉n秒前的所有請求,檢視集合是否滿了,如果沒滿,則通過請求,並放入集合,如果滿了就觸發拒絕策略。

分布式環境下的滑動日誌的實現需要考慮如下幾個問題:

我們的演算法其實已經簡化了儲存,但是對於高併發的場景,要快取的請求可能會很多(例如限制每秒十萬的請求,那麼這個快取的大小是否就應該能儲存十萬個請求?),這個快取應該如何實現?

高併發場景下,對於這個集合的刪除掉n秒前的所有請求的這個操作,需要速度非常快。如果你的快取集合實現對於按照時間戳刪除這個操作比較慢,可以快取多一點請求,定時清理刪除n秒前的所有請求而不是每次請求到來都刪除。請求到來的時候,檢視b個之前的請求是否存在並且時間差小於n秒,存在並且小於代表應該觸發限流策略。

5. 滑動視窗(滑動日誌 + 固定視窗)

前面的滑動日誌,我們提到了乙個問題 - 要快取的請求可能會很多。也許在我們的架構內不能使用乙個恰當的快取來實現,我們可以通過滑動視窗這個方法來減少要儲存的請求數量,並減少集合大小減少同乙個集合上面的併發。

演算法大概是:假設n秒內最多處理b個請求。我們可以將n秒切分成每個大小為m毫秒得時間片,只有最新的時間片內快取請求和時間戳,之前的時間片內只保留乙個請求量的數字。這樣可以大大優化儲存,小幅度增加計算量。對於臨界條件,就是之前已經有了n/m個時間片,計算n秒內請求量時可以計算當前時間片內經過時間的百分比,假設是 25%,那麼就取開頭的第乙個時間片的請求量的 75% 進行計算。

每日一刷,輕鬆提公升技術,斬獲各種offer:

請你談談為什麼分布式系統需要限流器

限流器是一種限制某種操作在一定時間內的執行次數 例如每秒鐘5次 或者執行量 例如每秒鐘1g大小的資料 的機制。限流器是一種防禦性的程式設計實現方式,在大資料量高併發訪問時,經常會出現服務或介面面對暴漲的請求而不可用的情況,甚至引發連鎖反映導致整個系統崩潰。此時你需要使用的技術手段之一就是限流,當請求...

分布式系統中的分布式事務

分布式事務中可以借助mq訊息系統來進行事務控制,這一點與可靠訊息最終一致方案一樣。看來mq中介軟體確實在乙個分布式系統架構中,扮演者重要的角色。最大努力通知方案是比較簡單的分布式事務方案,它本質上就是通過定期校對,實現資料一致性。中介軟體如何保證訊息的一致性 問題的問法多種多樣,怎麼保證兩個伺服器的...

分布式系統限流 從原理到實戰

在分布式系統中,流量控制,是保護系統 避免系統被流量沖垮的重要手段。但其實,分布式系統提供服務,和銀行處理業務類似,請求相當於辦理業務的客戶,如果櫃員處理的速度較慢,那新到的客戶就需要排隊。限流則意味著對排隊的情況進行控制,而控制的方式有很多 比如增加處理業務的櫃員數量,或者控制請求的速率 即 qp...