Spring Boot接口限流的常用算法及特點
前言
在一個高并發(fā)系統(tǒng)中對流量的把控是非常重要的,當巨大的流量直接請求到我們的服務(wù)器上沒多久就可能造成接口不可用,不處理的話甚至?xí)斐烧麄€應(yīng)用不可用。
那么何為限流呢?顧名思義,限流就是限制流量,就像你寬帶包了1個G的流量,用完了就沒了。通過限流,我們可以很好地控制系統(tǒng)的qps,從而達到保護系統(tǒng)的目的。本篇文章將會介紹一下常用的限流算法以及他們各自的特點。
算法介紹
計數(shù)器法
計數(shù)器法是限流算法里最簡單也是最容易實現(xiàn)的一種算法。比如我們規(guī)定,對于A接口來說,我們1分鐘的訪問次數(shù)不能超過100個。那么我們可以這么做:在一開始的時候,我們可以設(shè)置一個計數(shù)器counter,每當一個請求過來的時候,counter就加1,如果counter的值大于100并且該請求與第一個請求的間隔時間還在1分鐘之內(nèi),那么說明請求數(shù)過多;如果該請求與第一個請求的間隔時間大于1分鐘,且counter的值還在限流范圍內(nèi),那么就重置counter,具體算法的示意圖如下:

具體的偽代碼如下:

這個算法雖然簡單,但是有一個十分致命的問題,那就是臨界問題,我們看下圖:

從上圖中我們可以看到,假設(shè)有一個惡意用戶,他在0:59時,瞬間發(fā)送了100個請求,并且1:00又瞬間發(fā)送了100個請求,那么其實這個用戶在1秒里面,瞬間發(fā)送了200個請求。我們剛才規(guī)定的是1分鐘最多100個請求,也就是每秒鐘最多1.7個請求,用戶通過在時間窗口的重置節(jié)點處突發(fā)請求,可以瞬間超過我們的速率限制。用戶有可能通過算法的這個漏洞,瞬間壓垮我們的應(yīng)用。
聰明的朋友可能已經(jīng)看出來了,剛才的問題其實是因為我們統(tǒng)計的精度太低。那么如何很好地處理這個問題呢?或者說,如何將臨界問題的影響降低呢?我們可以看下面的滑動窗口算法。
滑動窗口
滑動窗口,又稱rolling window。為了解決這個問題,我們引入了滑動窗口算法。如果學(xué)過TCP網(wǎng)絡(luò)協(xié)議的話,那么一定對滑動窗口這個名詞不會陌生。下面這張圖,很好地解釋了滑動窗口算法:

在上圖中,整個紅色的矩形框表示一個時間窗口,在我們的例子中,一個時間窗口就是一分鐘。然后我們將時間窗口進行劃分,比如圖中,我們就將滑動窗口劃成了6格,所以每格代表的是10秒鐘。每過10秒鐘,我們的時間窗口就會往右滑動一格。每一個格子都有自己獨立的計數(shù)器counter,比如當一個請求在0:35秒的時候到達,那么0:30~0:39對應(yīng)的counter就會加1。
那么滑動窗口怎么解決剛才的臨界問題的呢?我們可以看上圖,0:59到達的100個請求會落在灰色的格子中,而1:00到達的請求會落在橘黃色的格子中。當時間到達1:00時,我們的窗口會往右移動一格,那么此時時間窗口內(nèi)的總請求數(shù)量一共是200個,超過了限定的100個,所以此時能夠檢測出來觸發(fā)了限流。
我再來回顧一下剛才的計數(shù)器算法,我們可以發(fā)現(xiàn),計數(shù)器算法其實就是滑動窗口算法。只是它沒有對時間窗口做進一步地劃分,為60s。
由此可見,當滑動窗口的格子劃分的越多,那么滑動窗口的滾動就越平滑,限流的統(tǒng)計就會越精確。

漏桶算法
漏桶算法,又稱leaky bucket。為了理解漏桶算法,我們看一下對于該算法的示意圖:

從圖中我們可以看到,整個算法其實十分簡單。首先,我們有一個固定容量的桶,有水流進來,也有水流出去。對于流進來的水來說,我們無法預(yù)計一共有多少水會流進來,也無法預(yù)計水流的速度。但是對于流出去的水來說,這個桶可以固定水流出的速率。而且,當桶滿了之后,多余的水將會溢出。
我們將算法中的水換成實際應(yīng)用中的請求,我們可以看到漏桶算法天生就限制了請求的速度。當使用了漏桶算法,我們可以保證接口會以一個常速速率來處理請求。所以漏桶算法天生不會出現(xiàn)臨界問題。具體的偽代碼實現(xiàn)如下:

令牌桶算法
令牌桶算法,又稱token bucket。為了理解該算法,我們再來看一下算法的示意圖:

從圖中我們可以看到,令牌桶算法比漏桶算法稍顯復(fù)雜。首先,我們有一個固定容量的桶,桶里存放著令牌(token)。桶一開始是空的,token以一個固定的速率r往桶里填充,直到達到桶的容量,多余的令牌將會被丟棄。每當一個請求過來時,就會嘗試從桶里移除一個令牌,如果沒有令牌的話,請求無法通過。
具體的偽代碼實現(xiàn)如下:

RateLimiter實現(xiàn)
對于令牌桶的代碼實現(xiàn),可以直接使用Guava包中的RateLimiter。

相關(guān)變種
若仔細研究算法,我們會發(fā)現(xiàn)我們默認從桶里移除令牌是不需要耗費時間的。如果給移除令牌設(shè)置一個延時時間,那么實際上又采用了漏桶算法的思路。Google的guava庫下的SmoothWarmingUp類就采用了這個思路。
臨界問題
我們再來考慮一下臨界問題的場景。在0:59秒的時候,由于桶內(nèi)積滿了100個token,所以這100個請求可以瞬間通過。但是由于token是以較低的速率填充的,所以在1:00的時候,桶內(nèi)的token數(shù)量不可能達到100個,那么此時不可能再有100個請求通過。所以令牌桶算法可以很好地解決臨界問題。下圖比較了計數(shù)器(左)和令牌桶算法(右)在臨界點的速率變化。我們可以看到雖然令牌桶算法允許突發(fā)速率,但是下一個突發(fā)速率必須要等桶內(nèi)有足夠的token后才能發(fā)生:
總結(jié)
計數(shù)器 VS 滑動窗口
計數(shù)器算法是最簡單的算法,可以看成是滑動窗口的低精度實現(xiàn)?;瑒哟翱谟捎谛枰鎯Χ喾莸挠嫈?shù)器(每一個格子存一份),所以滑動窗口在實現(xiàn)上需要更多的存儲空間。也就是說,如果滑動窗口的精度越高,需要的存儲空間就越大。
漏桶算法 VS 令牌桶算法
漏桶算法和令牌桶算法最明顯的區(qū)別是令牌桶算法允許流量一定程度的突發(fā)。因為默認的令牌桶算法,取走token是不需要耗費時間的,也就是說,假設(shè)桶內(nèi)有100個token時,那么可以瞬間允許100個請求通過。
令牌桶算法由于實現(xiàn)簡單,且允許某些流量的突發(fā),對用戶友好,所以被業(yè)界采用地較多。當然我們需要具體情況具體分析,只有最合適的算法,沒有最優(yōu)的算法。
到此這篇關(guān)于Spring Boot接口限流的常用算法及特點的文章就介紹到這了,更多相關(guān)Spring Boot接口限流算法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis Plus 字段為空值時執(zhí)行更新方法未更新解決方案
這篇文章主要介紹了Mybatis Plus 字段為空值時執(zhí)行更新方法未更新解決方案,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
SpringBoot集成SpringSecurity和JWT做登陸鑒權(quán)的實現(xiàn)
這篇文章主要介紹了SpringBoot集成SpringSecurity和JWT做登陸鑒權(quán)的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
詳解SpringBoot通過restTemplate實現(xiàn)消費服務(wù)
本篇文章主要介紹了詳解使用RestTemplate消費spring boot的Restful服務(wù),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-01
idea報錯:程序包org.springframework.web.bind.annotation不存在
在用本地的maven倉庫的時候會org.springframework.web.bind.annotation不存在的錯誤,本文就詳細的介紹一下解決方法,感興趣的可以了解下2023-08-08

