【181002】高併發場景下的限流策略
在高併發的場景下,我們的優化和保護系統的方式通常有:多級快取、資源隔離、熔斷降級、限流等等。
今天我們來聊聊限流。
引子
我們為什麼需要限流?
舉個比較簡單的例子,正常來說,一個員工A他每天能夠處理的工作是10個,突然某一天來了100個工作量,這時候,如果員工A還處理100個,只有一種可能,這個員工被壓垮。
如果我們能預先知道會有100個任務回來,我們可以做加員工數或定義訊息佇列等等。但是我們很多時候無法預料這些意外的。根據 墨菲定律 ,壞事往往會接踵而來,有可能某個點掛了會引起全域性的掛掉(雪崩)。因此我們不得不對我們的系統做一些保護措施。限流是其中之一。
針對秒殺這類場景,我們也可以做一些限流措施,而不影響到系統全域性。
限流方式之計數器(滑動視窗協議)
思路:限速,我們可能第一個想到的應該是,我通過一個計數器,進行技術,如果超過了計數器閥值,表示速度太快了。一秒一個計數器。

計數器.png
為了便於閱讀,我只截圖了主要的程式碼片段。

程式碼片段
這樣有個問題就是:粒度太大了,不均勻,針對1秒一下的,沒法辨析。
我們能不能把粒度拆細了,1秒拆成10個100毫秒。每一個100毫秒有一個計數器。瞭解TCP/IP的應該知道,TCP/IP為了增加傳輸速度和控制傳輸速度,有個叫“滑動視窗協議”。
就算拆得再細,也無法解決勻速限制速度的問題。
而且還有個臨界點問題,比如假如,一秒限制10個請求,在第1秒鐘,第2秒 之間,第1秒後半段時間10個請求,第2秒前半段10個請求,那第1秒後半段+第2秒前半段時間組成的一秒鐘裡就有20個請求,沒有起到限速的作用。
有沒有更好的辦法呢?
限速方式之漏桶演算法
在生活中,如果一桶有一個細眼,我們往裡面裝水,可以看到水是一滴一滴勻速的下落的,哪我們能不能通過程式來實現這種方式呢。
思路:桶為容器,一滴水為一請求。如果桶滿了就拒絕請求,沒滿處理請求。

漏桶演算法.png
程式碼片段

漏桶演算法
在段程式碼中
- 首先計算這次請求與上次請求來的時候,總共漏了多少水。
- 看一下桶裡面還剩多少水,有沒有溢位。
- 如果溢位了拒絕請求,如果沒有添加當前一滴水。處理請求。
對於很多應用場景來說,除了要求能夠限制資料的平均傳輸速率外,還要求允許某種程度的突發傳輸。這時候漏桶演算法可能就不合適了,令牌桶演算法更為適合。
什麼意思呢?就是說我服務前面閒了很久,突然來了很多請求(在桶的容量內),我得快速的把這些處理了。
限速方式之令牌桶演算法
思路:勻速的產生令牌,往桶裡面丟,每次請求來,看是否有多餘的令牌。如果有獲取令牌執行正常業務,偌沒有限速。

令牌桶.png
程式碼片段

令牌桶程式碼
通過這種方式可以允許瞬時的大量處理,然後做限速處理。
- 請求來的時候先計算目前放入桶中的令牌數,這裡計算,就可以不用啟動一個執行緒勻速放置令牌了,這個叫惰性計算。
- 然後計算桶擁有的令牌數。然後獲取令牌。做拒絕還是處理動作。
以上程式碼,你均可以在
ofollow,noindex">https://github.com/hirudy/java_lib/tree/master/src/main/java/com/hirudy/limiter
找到。
單機限速器RateLimiter
安利大家一個高效的限速器。
google的基礎庫guava中包含了一個基於令牌桶的限速器RateLimiter。使用也很簡單。

測試樣例
都看到這裡了,關注個公眾號吧,一起交流學習

微信公眾號rudy_tan_home