java介面限流演算法

來源:互聯網
上載者:User

標籤:特殊   blog   線程   bubuko   應用   最大   period   cin   預設值   

0. 前言

常見的限流演算法有:令牌桶、漏桶。計數器也可以進行粗暴限流實現。

1. 演算法介紹1.1 令牌桶演算法

令牌桶演算法是一個存放固定容量令牌的桶,按照固定速率往桶裡添加令牌。令牌桶演算法的描述如下:

  • 假設限制2r/s,則按照500毫秒的固定速率往桶中添加令牌;
  • 桶中最多存放b個令牌,當桶滿時,新添加的令牌被丟棄或拒絕;
  • 當一個n個位元組大小的資料包到達,將從桶中刪除n個令牌,接著資料包被發送到網路上;
  • 如果桶中的令牌不足n個,則不會刪除令牌,且該資料包將被限流(要麼丟棄,要麼緩衝區等待)。

1.2 漏桶演算法

漏桶作為計量工具(The Leaky Bucket Algorithm as a Meter)時,可以用於流量整形(Traffic Shaping)和流量控制(TrafficPolicing),漏桶演算法的描述如下:

  • 一個固定容量的漏桶,按照常量固定速率流出水滴;
  • 如果桶是空的,則不需流出水滴;
  • 可以以任意速率流入水滴到漏桶;
  • 如果流入水滴超出了桶的容量,則流入的水滴溢出了(被丟棄),而漏桶容量是不變的。

1.3 總結

令牌桶和漏桶對比:

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

另外有時候我們還使用計數器來進行限流,主要用來限制總並發數,比如資料庫連接池、線程池、秒殺的並發數;只要全域總請求數或者一定時間段的總請求數設定的閥值則進行限流,是簡單粗暴的總數量限流,而不是平均速率限流。

2. 演算法實現

《java介面限流小結》中的限流方式都不能很好地應對突發請求,即瞬間請求可能都被允許從而導致一些問題;因此在一些情境中需要對突發請求進行整形,整形為平均速率請求處理(比如5r/s,則每隔200毫秒處理一個請求,平滑了速率)。這個時候有兩種演算法滿足我們的情境:令牌桶和漏桶演算法。Guava架構提供了令牌桶演算法實現,可直接拿來使用。

Guava RateLimiter提供了令牌桶演算法實現:平滑突發限流(SmoothBursty)和平滑預熱限流(SmoothWarmingUp)實現。

2.1 SmoothBursty
RateLimiter limiter = RateLimiter.create(5);System.out.println(limiter.acquire());System.out.println(limiter.acquire());System.out.println(limiter.acquire());System.out.println(limiter.acquire());System.out.println(limiter.acquire());System.out.println(limiter.acquire());

將得到類似如下的輸出:

0.00.1989030.1954630.197120.1992520.196105

1、RateLimiter.create(5) 表示桶容量為5且每秒新增5個令牌,即每隔200毫秒新增一個令牌;
2、limiter.acquire()表示消費一個令牌,如果當前桶中有足夠令牌則成功(傳回值為0),如果桶中沒有令牌則暫停一段時間,比如發令牌間隔是200毫秒,則等待200毫秒後再去消費令牌(如上測試案例返回的為0.198239,差不多等待了200毫秒桶中才有令牌可用),這種實現將突發請求速率平均為了固定請求速率。

再看一個突發樣本:

RateLimiter limiter = RateLimiter.create(5);System.out.println(limiter.acquire(5));System.out.println(limiter.acquire(1));System.out.println(limiter.acquire(1));

將得到類似如下的輸出:

0.00.9987290.19379

limiter.acquire(5)表示桶的容量為5且每秒新增5個令牌,令牌桶演算法允許一定程度的突發,所以可以一次性消費5個令牌,但接下來的limiter.acquire(1)將等待差不多1秒桶中才能有令牌,且接下來的請求也整形為固定速率了。

RateLimiter limiter = RateLimiter.create(5);System.out.println(limiter.acquire(10));System.out.println(limiter.acquire(1));System.out.println(limiter.acquire(1));

將得到類似如下的輸出:

0.01.9989220.19615

同上邊的例子類似,第一秒突發了10個請求,令牌桶演算法也允許了這種突發(允許消費未來的令牌),但接下來的limiter.acquire(1)將等待差不多2秒桶中才能有令牌,且接下來的請求也整形為固定速率了。

接下來再看一個突發的例子:

RateLimiter limiter = RateLimiter.create(2);System.out.println(limiter.acquire());Thread.sleep(2000L);System.out.println(limiter.acquire());System.out.println(limiter.acquire());System.out.println(limiter.acquire());System.out.println(limiter.acquire());System.out.println(limiter.acquire());

接下來再看一個突發的例子:

0.00.00.00.00.4997380.496078

1、建立了一個桶容量為2且每秒新增2個令牌;
2、首先調用limiter.acquire()消費一個令牌,此時令牌桶可以滿足(傳回值為0);
3、然後線程暫停2秒,接下來的兩個limiter.acquire()都能消費到令牌,第三個limiter.acquire()也同樣消費到了令牌,到第四個時就需要等待500毫秒了。

此處可以看到我們設定的桶容量為2(即允許的突發量),這是因為SmoothBursty中有一個參數:最大突發秒數(maxBurstSeconds)預設值是1s,突發量/桶容量=速率*maxBurstSeconds,所以本樣本桶容量/突發量為2,例子中前兩個是消費了之前積攢的突發量,而第三個開始就是正常計算的了。令牌桶演算法允許將一段時間內沒有消費的令牌暫存到令牌桶中,留待未來使用,並允許未來請求的這種突發。

SmoothBursty通過平均速率和最後一次新增令牌的時間計算出下次新增令牌的時間的,另外需要一個桶暫存一段時間內沒有使用的令牌(即可以突發的令牌數)。另外RateLimiter還提供了tryAcquire方法來進行無阻塞或可逾時的令牌消費。

因為SmoothBursty允許一定程度的突發,會有人擔心如果允許這種突發,假設突然間來了很大的流量,那麼系統很可能扛不住這種突發。因此需要一種平滑速率的限流工具,從而系統冷啟動後慢慢的趨於平均固定速率(即剛開始速率小一些,然後慢慢趨於我們設定的固定速率)。Guava也提供了SmoothWarmingUp來實現這種需求,其可以認為是漏桶演算法,但是在某些特殊情境又不太一樣。

2.2 SmoothWarmingUp

SmoothWarmingUp建立方式:RateLimiter.create(doublepermitsPerSecond, long warmupPeriod, TimeUnit unit)
permitsPerSecond表示每秒新增的令牌數,warmupPeriod表示在從冷啟動速率過渡到平均速率的時間間隔。
樣本如下:

RateLimiter limiter = RateLimiter.create(5, 1000, TimeUnit.MILLISECONDS);for(int i = 1; i < 5;i++) {    System.out.println(limiter.acquire());}Thread.sleep(1000L);for(int i = 1; i < 5;i++) {    System.out.println(limiter.acquire());}

將得到類似如下的輸出:

0.00.5189160.3567430.2191830.00.5199270.3547510.214637

速率是梯形上升速率的,也就是說冷啟動時會以一個比較大的速率慢慢到平均速率;然後趨於平均速率(梯形下降到平均速率)。可以通過調節warmupPeriod參數實現一開始就是平滑固定速率。

到此應用級限流的一些方法就介紹完了。假設將應用部署到多台機器,應用級限流方式只是單應用內的請求限流,不能進行全域限流。因此我們需要分布式限流和接入層限流來解決這個問題。

參考:
http://jinnianshilongnian.iteye.com/blog/2305117

java介面限流演算法

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.