摘要:接口限流的常用算法計數器法計數器法是限流算法里最簡單也是最容易實現的一種算法。由此可見,當滑動窗口的格子劃分的越多,那么滑動窗口的滾動就越平滑,限流的統計就會越精確。漏桶算法漏桶算法,又稱。
接口限流
什么是接口限流
那么什么是限流呢?顧名思義,限流就是限制流量,包括并發的流量和一定時間內的總流量,就像你寬帶包了1個G的流量,用完了就沒了,所以控制你的使用頻率和單次使用的總消耗。通過限流,我們可以很好地控制系統的qps,從而達到保護系統或者接口服務器穩定的目的。
接口限流的常用算法
計數器法
計 數器法是限流算法里最簡單也是最容易實現的一種算法。比如我們規定,對于A接口來說,我們1分鐘的訪問次數不能超過100個。那么我們可以這么做:在一開 始的時候,我們可以設置一個計數器counter,每當一個請求過來的時候,counter就加1,如果counter的值大于100并且該請求與第一個 請求的間隔時間還在1分鐘之內,那么說明請求數過多;如果該請求與第一個請求的間隔時間大于1分鐘,且counter的值還在限流范圍內,那么就重置 counter,具體算法的示意圖如下:
偽代碼如下
class CounterDemo{ private $timeStamp; public $reqCount=0; public $limit=100;//時間窗口內最大請求數 public $interval=1000; //時間窗口 ms public function __construct() { $this->timeStamp = time(); } public function grant(){ $now=time(); if($now<$this->timeStamp+$this->interval){ //時間窗口內 $this->reqCount++; return $this->reqCount<=$this->limit; }else{ // 超時后重置 $this->timeStamp=time(); $this->reqCount=1; return true; } } }
這個算法看起來簡單但是有個很嚴重的問題,那就是臨界問題,看下圖
從上圖中我們可以看到,假設有一個惡意用戶,他在0:59時,瞬間發送了100個請求,并且1:00又瞬間發送了100個請求,那么其實這個用戶在 1秒里面,瞬間發送了200個請求。我們剛才規定的是1分鐘最多100個請求,也就是每秒鐘最多1.7個請求,用戶通過在時間窗口的重置節點處突發請求, 可以瞬間超過我們的速率限制。用戶有可能通過算法的這個漏洞,瞬間壓垮我們的應用。
聰明的朋友可能已經看出來了,剛才的問題其實是因為我們統計的精度太低。那么如何很好地處理這個問題呢?或者說,如何將臨界問題的影響降低呢?我們可以看下面的滑動窗口算法。
滑動窗口算法
直接上圖吧
在上圖中,整個紅色的矩形框表示一個時間窗口,在我們的例子中,一個時間窗口就是一分鐘。然后我們將時間窗口進行劃分,比如圖中,我們就將滑動窗口 劃成了6格,所以每格代表的是10秒鐘。每過10秒鐘,我們的時間窗口就會往右滑動一格。每一個格子都有自己獨立的計數器counter,比如當一個請求 在0:35秒的時候到達,那么0:30~0:39對應的counter就會加1。
那么滑動窗口怎么解決剛才的臨界問題的呢?我們可以看上圖,0:59到達的100個請求會落在灰色的格子中,而1:00到達的請求會落在橘黃色的格 子中。當時間到達1:00時,我們的窗口會往右移動一格,那么此時時間窗口內的總請求數量一共是200個,超過了限定的100個,所以此時能夠檢測出來觸 發了限流。
我再來回顧一下剛才的計數器算法,我們可以發現,計數器算法其實就是滑動窗口算法。只是它沒有對時間窗口做進一步地劃分,所以只有1格。
由此可見,當滑動窗口的格子劃分的越多,那么滑動窗口的滾動就越平滑,限流的統計就會越精確。
漏桶算法
漏桶算法,又稱leaky bucket。為了理解漏桶算法,我們看一下維基百科上的對于該算法的示意圖:
從圖中我們可以看到,整個算法其實十分簡單。首先,我們有一個固定容量的桶,有水流進來,也有水流出 去。對于流進來的水來說,我們無法預計一共有多 少水會流進來,也無法預計水流的速度。但是對于流出去的水來說,這個桶可以固定水流出的速率。而且,當桶滿了之后,多余的水將會溢出。
我們將算法中的水換成實際應用中的請求,我們可以看到漏桶算法天生就限制了請求的速度。當使用了漏桶算法,我們可以保證接口會以一個常速速率來處理請求。所以漏桶算法天生不會出現臨界問題。具體的偽代碼實現如下:
class LeakyDemo{ private $timeStamp; public $capacity;// 桶的容量 public $rate; // 水漏出的速度 public $water;// 當前水量(當前累積請求數) public function __construct() { $this->timeStamp = time(); } public function grant(){ $now=time(); $this->water=max(0,$this->water-($now-$this->timeStamp)*$this->rate);// 先執行漏水,計算剩余水量 $this->timeStamp=$now; if(($this->water+1)<$this->capacity){ // 嘗試加水,并且水還未滿 $this->water+=1; return true; }else{ // 水滿,拒絕加水 return false; } } }
令牌桶算法
令牌桶算法,又稱token bucket。為了理解該算法,我們再來看一下維基百科上對該算法的示意圖:
從圖中我們可以看到,令牌桶算法比漏桶算法稍顯復雜。首先,我們有一個固定容量的桶,桶里存放著令牌(token)。桶一開始是空的,token以 一個固定的速率r往桶里填充,直到達到桶的容量,多余的令牌將會被丟棄。每當一個請求過來時,就會嘗試從桶里移除一個令牌,如果沒有令牌的話,請求無法通 過。
具體的偽代碼實現如下:
class TokenBucketDemo{ private $timeStamp; public $capacity;// 桶的容量 public $rate; // 令牌放入的速度 public $tokens;// 當前令牌的數量 public function __construct() { $this->timeStamp = time(); } public function grant(){ $now=time(); $this->tokens=min(0,$this->tokens+($now-$this->timeStamp)*$this->rate);// 先執行漏水,計算剩余水量 $this->timeStamp=$now; if($this->tokens<1){ // 若不到1個令牌,則拒絕 return false; }else{ // 還有令牌,領取令牌 $this->tokens -= 1; return true; } } }
臨界問題
臨界問題
我們再來考慮一下臨界問題的場景。在0:59秒的時候,由于桶內積滿了100個token,所以這100個請求可以瞬間通過。但是由于token是以較低的 速率填充的,所以在1:00的時候,桶內的token數量不可能達到100個,那么此時不可能再有100個請求通過。所以令牌桶算法可以很好地解決臨界問 題。下圖比較了計數器(左)和令牌桶算法(右)在臨界點的速率變化。我們可以看到雖然令牌桶算法允許突發速率,但是下一個突發速率必須要等桶內有足夠的 token后才能發生
總結
計數器算法是最簡單的算法,可以看成是滑動窗口的低精度實現。滑動窗口由于需要存儲多份的計數器(每一個格子存一份),所以滑動窗口在實現上需要更多的存儲空間。也就是說,如果滑動窗口的精度越高,需要的存儲空間就越大。
漏桶算法 VS 令牌桶算法
漏桶算法和令牌桶算法最明顯的區別是令牌桶算法允許流量一定程度的突發。因為默認的令牌桶算法,取走token是不需要耗費時間的,也就是說,假設桶內有100個token時,那么可以瞬間允許100個請求通過。
令牌桶算法由于實現簡單,且允許某些流量的突發,對用戶友好,所以被業界采用地較多。當然我們需要具體情況具體分析,只有最合適的算法,沒有最優的算法。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/22455.html
摘要:歡迎訪問我的歡迎訪問我的內容所有原創文章分類匯總及配套源碼,涉及等本篇概覽本篇概覽本文是實戰系列的第八篇,經過前面的學習,咱們對過濾器已了解得差不多,今天來補全過濾器的最后一個版塊限流默認的限流器是基于實現的,限流算法是大家熟悉的令牌桶關于歡迎訪問我的GitHubhttps://github.com/zq2599/blog_demos內容:所有原創文章分類匯總及配套源碼,涉及Java、Doc...
摘要:計數限流算法無論固定窗口還是滑動窗口核心均是對請求進行計數,區別僅僅在于對于計數時間區間的處理。令牌桶限流實現原理令牌桶限流的實現原理在有詳細說明。因此由此為入口進行分析。目前可返回的實現子類包括及兩種,具體不同下文詳細分析。 限流 限流一詞常用于計算機網絡之中,定義如下: In computer networks, rate limiting is used to control t...
閱讀 3794·2023-04-25 16:32
閱讀 2194·2021-09-28 09:36
閱讀 2035·2021-09-06 15:02
閱讀 673·2021-09-02 15:21
閱讀 918·2019-08-30 15:56
閱讀 3513·2019-08-30 15:45
閱讀 1708·2019-08-30 13:09
閱讀 379·2019-08-29 16:05