閱讀本文大概需要 3 分鐘。
作者:會寫代碼的一條魚
原文鏈接:www.toutiao.com/i6692665902485209612
對於一個分佈式系統而言,如何保證系統的穩定可靠,永遠都是頭等大事。緩存、限流和降級是最有效也是我們最常用的手段。
今天我們就一起來看看分佈式系統是如何進行限流的。
爲什麼要限流
原因很簡單,資源是有限的,我們的系統的接待能力也是有限的,對於那些已經超出系統接待能力的請求我們應該儘可能早的識別出來並讓其等待或拒絕這些請求。
當大流量進入系統而我們又不進行限流,那麼處理請求能力最差的一個子系統將會最先宕機,進而導致依賴這個子系統的其它系統也跟着宕機,最終導致整個系統全面癱瘓,這就是系統雪崩效應。
限流算法
限流的前提是我們能夠準確的計算出過去一段時間的請求數量,然後根據系統負載能力來判斷接下來的請求是否放行。
常用的限流算法可以分爲兩大類:計數法和桶算法。
其中計數法又可以分爲計數器和滑動窗口計數法,桶算法則分爲漏桶法和令牌桶法。
接下來我們就深入的分析一下這些限流算法的特點。
計數器
這種限流算法是最爲簡單直接的了。直接記錄一下當前週期內的請求個數,如果請求個數超出了閾值,那麼就限制請求,如果沒有超出,就放行。
如下圖所示:
計數器法雖然實現上非常簡單,也很容易理解,但是它的缺點也是非常明顯的。我們假設一種情況:
系統線路的 QPS 爲 100,第一秒有 90 個請求,並且所有的請求都在最後 100ms 進入,這個時候請求沒有達到閾值,是不會限流的。
緊接着第二秒也有 90 個請求,不過全部集中在前 100ms 進入,這個時候也沒有達到閾值,也不會限流。
然而如果我們全局分析,會發現在短短的 200ms 內進入了 180 個請求,這顯然是遠遠超過了限流閾值 100 的。
具體情況如下圖所示:
顯然計數器法在要求比較高的場景下是不適用的。
滑動窗口法
滑動窗口法是在計數器法的基礎上演進而來的,也是採用計數的方式來統計過去一段時間的請求數。
與計數器法不一樣的地方是:滑動窗口計數會把計數窗口進行分割,比如分割成兩份、10 份等,分割的越小,精度越高。
如下圖所示:
上圖展示了把統計窗口均分爲 10 等分的情況,假設統計窗口爲 1秒,那麼每一小格代表的就是 100ms 的請求計數,最近 1 秒中的請求總數就等於最近 10 小格的統計數之和。
每過去 100ms,統計窗口就向右滑動一小格(這就是滑動窗口法的由來),最新的數據記錄在最右邊位置,最左一格的數據將會被丟棄(具體實現上會有所差異)。
其實滑動窗口法並沒有完全消除計數器法中遇到的問題,它只是減小了影響。
假設限流 QPS 大小爲 X,窗口均分爲 N 份,那麼理論上可以達到的峯值 QPS爲X * (N + 1) / N,它顯然是大於 X 的。
不過我們多均分幾份以後,影響就會大大減少。
漏桶法
漏桶法非常的簡單,也非常的形象。我們可以把整個系統看成一個水桶,進來的請求理解爲往桶裏注入水,處理請求就是桶中的的流出。
漏桶法就是不管注入水(請求進入)的快慢如何,我只按照恆定的流水出水(處理請求)。
固定線程個數的線程池就是我們平時接觸的比較多的漏桶法限流的例子,這種情況中不管需要處理的任務有多少,線程池最多隻會運行固定個數的任務,其餘的任務要麼被拒絕要麼等待。
令牌桶算法
令牌桶算法就是系統會安裝固定的速率往桶中添加令牌,請求的時候先到桶裏拿一個令牌,如果能夠拿到令牌就表示可以進行請求處理,如果桶裏沒有令牌了,就表明需要限流了。
常用限流庫
1、Google 的 Guava 工具集中有一個 RateLimiter 限流器,在令牌桶的算法基礎上進行改良實現的。
2、阿里開源的 Sentinel 也具有限流的功能,採用的是滑動窗口算法進行限流。
·END·
程序員的成長之路
路雖遠,行則必至
本文原發於 同名微信公衆號「程序員的成長之路」,回覆「1024」你懂得,給個讚唄。
回覆 [ 520 ] 領取程序員最佳學習方式
回覆 [ 256 ] 查看 Java 程序員成長規劃