高併發下的限流策略(2)

限流策略通常是用來在高qps下進行流量限制的,常見的方式有計數器、令牌桶、漏桶。

在這次活動中我負責的模塊是控制的對下游的流量,我們可以讓那些請求選擇丟棄、等待或者降級這些限流算法可以自行實現也可以利用現有的限流工具,比如說Guava的令牌桶,具體看場景需求吧,下面來看一下這幾種限流策略,再說說我寫的限流方式。

1、計數器限流

種方式比較粗暴,相當於有一個計數器來控制單秒請求數,也就是qps定義的方式。比如說限流2000,每秒鐘對於計數器進行置0操作,當一秒內到達2000時就不再接受請求了。這樣能保證較長時間短的流量均勻,但是單秒內部實際上是不均勻的,可能這2000個請求,在前0.1s就處理完成了,後面的都是被丟掉的,並且峯值qps 可能是達到2w的。

2、令牌桶限流

令牌桶限流是指我們可以設立一個令牌桶,然後以固定的速率往令牌桶中添加令牌,令牌桶滿則不添加。請求到來時檢查如果令牌桶中有令牌則取走令牌,發起請求。假設要限定 2000 qps,則1/2000 的速率向令牌桶添加令牌,也可以1/1000 一次性添加兩個令牌,以此類推。令牌桶在持續高qps 下是沒問題的,可以把流量限制的比較均勻。但是面對突發流量時,流量桶裏是滿的,可能一瞬間把令牌搶空完成請求,這裏的問題和計數器限流實際上是一樣的,峯值可能遠遠大於2000,所以對於突發流量是限不住的。下面看看令牌桶的示意圖:其實我感覺令牌桶更像是優化後的計數器限流,只不過時間窗口由1s變的更細了

 

 

 

3、漏斗限流

這個是使用最多的一種限流算法,通常用來流量整形或者流量控制,看起來和令牌桶比較像,但是差異還是比較大的。漏斗往桶裏加的是請求,不是令牌,相當於新請求到達時放到桶中,如果桶滿了則溢出請求,桶以勻速漏出請求進行處理,比如qps 2000,則1/2000 s 漏出一個請求進行處理。這種限流方式比較穩定,但是需要維護一個請求隊列或者任務隊列。

漏斗&令牌桶比較:

  1. 令牌桶是按照固定速率往桶中添加令牌,請求是否被處理需要看桶中令牌是否足夠,當令牌數減爲零時則拒絕新的請求;
  2. 漏桶則是按照常量固定速率流出請求,流入請求速率任意,當流入的請求數累積到漏桶容量時,則新流入的請求被拒絕;
  3. 令牌桶限制的是平均流入速率(允許突發請求,只要有令牌就可以處理,支持一次拿3個令牌,4個令牌),並允許一定程度突發流量;
  4. 漏桶限制的是常量流出速率(即流出速率是一個固定常量值,比如都是1的速率流出,而不能一次是1,下次又是2),從而平滑突發流入速率;
  5. 令牌桶允許一定程度的突發,而漏桶主要目的是平滑流入速率;
  6. 兩個算法實現可以一樣,但是方向是相反的,對於相同的參數得到的限流效果是一樣的。

看完幾種限流策略原型之後,放到具體的業務場景中看算法的選擇及我們需要作出的改動。

業務端限流:

業務端做限流的話,請求來源於上有系統,流量要求是比較平穩的,峯值不能太高,否則可能一瞬間打掛系統,令牌桶和計數器方式就不太合適了。因爲如果流量直接打到業務系統我們是沒法進行預估的,大概率會有突發流量,所以選擇直接使用流量桶就比較合適了。

consumer 控制對下游流量:

我所處的業務場景是nginx + lua 打入redis 則認爲成功,consumer端消費消息,然後持續固定qps對下游發起請求。這種場景下,把消費速率&對下游qps控制放在consumer端來做就比較合適了。首先我們從Redis或者日誌文件中讀取了數據,並且拼接了請求任務放到任務隊列中。然後線程池從任務隊列中取任務發起請求,首先我們需要控制加入任務隊列的速率,因爲加入的任務隊列的速度大於任務隊列的消費速度,肯定是會導致OOM產生的,

我這裏使用的是類似令牌桶的方式,一個線程取n個任務,然後線程內串行,幾個線程並行,這樣就一定程度上保證了不會出現令牌桶的流量不均問題了,同時減少了鎖的爭用。實際上使用漏斗算法也是合適的,但是對於這個場景來說,溢出任務實際上是不太好控制,需要讓請求的加入速率與消費速度相對保持一致,這一點控制不好很容易oom的,所以直接採用任務隊列 + 令牌桶實現是最方便控制也是最容易實現的,採用線程內一次取多個,串行發起請求的方式是可以一定程度上控制住流量的 實踐證明效果不錯。

 

參考:

https://cloud.tencent.com/developer/article/1477216

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章