前言
在高併發的場景下,我們的優化和保護系統的方式通常有:多級緩存、資源隔離、熔斷降級、限流等等。
今天我們來聊聊限流。
為什麼需要限流
舉個比較簡單的例子,正常來說,一個員工A他每天能夠處理的工作是10個,突然某一天來了100個工作量,這時候,如果員工A還處理100個,只有一種可能,這個員工被壓垮。
如果我們能預先知道會有100個任務會來,我們通過增加員工數或定義消息隊列等等來臨時解決。
但是我們很多時候無法預料這些意外的。根據墨菲定律,壞事往往會接踵而來,有可能某個點掛了會引起全局的掛掉(雪崩)。因此我們不得不對我們的系統做一些保護措施。限流是其中之一。
針對秒殺這類場景,我們也可以做一些限流措施,而不影響到系統全局。
限流方式之計數器(滑動窗口協議)
思路:限速,我們可能第一個想到的應該是,我通過一個計數器,進行技術,如果超過了計數器閥值,表示速度太快了。一秒一個計數器。
為了便於閱讀,我只截圖了主要的代碼片段。
這樣有個問題就是:粒度太大了,不均勻,針對1秒以下的,沒法辨析。
我們能不能把粒度拆細了,1秒拆成10個100毫秒。每一個100毫秒有一個計數器。瞭解TCP/IP的應該知道,TCP/IP為了增加傳輸速度和控制傳輸速度,有個叫“滑動窗口協議”。
就算拆得再細,也無法解決勻速限制速度的問題。
而且還有個臨界點問題,假如,一秒限制10個請求,在第1秒和第2秒之間,第1秒後半段時間10個請求,第2秒前半段10個請求,那第1秒後半段+第2秒前半段時間組成的一秒鐘裡就有20個請求,沒有起到限速的作用。
有沒有更好的辦法呢?
限速方式之漏桶算法
在生活中,如果一桶有一個細眼,我們往裡面裝水,可以看到水是一滴一滴勻速的下落的,哪我們能不能通過程序來實現這種方式呢。
思路:桶為容器,一滴水為一請求。如果桶滿了就拒絕請求,沒滿處理請求。
代碼片段
在段代碼中
- 首先計算這次請求與上次請求來的時候,總共漏了多少水。
- 看一下桶裡面還剩多少水,有沒有溢出。
- 如果溢出了拒絕請求,如果沒有添加當前一滴水。處理請求。
什麼意思呢?就是說我服務前面閒了很久,突然來了很多請求(在桶的容量內),我得快速的把這些處理了。
限速方式之令牌桶算法
思路:勻速的產生令牌,往桶裡面丟,每次請求來,看是否有多餘的令牌。如果有獲取令牌執行正常業務,偌沒有限速。
代碼片段
通過這種方式可以允許瞬時的大量處理,然後做限速處理。
- 請求來的時候先計算目前放入桶中的令牌數,這裡計算,就可以不用啟動一個線程勻速放置令牌了,這個叫惰性計算。
- 然後計算桶擁有的令牌數。然後獲取令牌。做拒絕還是處理動作。
單機限速器RateLimiter
安利大家一個高效的限速器。
google的基礎庫guava中包含了一個基於令牌桶的限速器RateLimiter。使用也很簡單。
關注我,私信回覆我“666"或者“Java架構"獲取免費的Java架構學習資料(裡面有高可用、高併發、高性能及分佈式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!
閱讀更多 Java高級架構進階 的文章