Guava與RateLimiter限流

什麼是Guava?

Guava是Google的一組核心Java庫,提供了很多設計精良、使用方便的工具類,它廣泛用於Google的大多數Java項目中,也被許多其他公司廣泛使用。其中就包含一款限流工具RateLimiter。
首先我們當然要引入他。

  <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>28.1-jre</version>
  </dependency>

什麼是限流

任何應用都有一定的訪問上限,如果請求速率突破這個上限,不但多餘的請求無法處理,甚至會壓垮系統所有的請求均無法處理,所以,對請求進行限流是非常有必要的。就跟百度雲限速一個道理。

常用限流算法

一種簡單的限流算法就是給出一個單位時間,然後使用一個計數器統計單位時間內收到的請求數量,當請求數量超過限制時,餘下的請求丟棄或者等待。但這種算法有一個嚴重的問題,就是很難控制邊界時間上的請求。假設時間單位是1秒,每秒請求不超過10個,如果在這一秒的前半秒沒有請求,後半秒有10個請求,下一秒的前半秒又有10個請求,那麼在這中間的一秒內,就會合理處理20個請求,而這明顯違反了限流的基本需求,這是一種簡單粗暴的總數量限流而不是平均限流。

所以,更爲一般化的限流算法有兩種,漏桶算法和令牌算法。
漏桶算法的基本思想是,利用一個緩存區,當有請求進入系統時,無論請求的速率如何,都先在緩存區內保存,然後以固定的流速流出緩存區進行處理。他的特點是無論外部請求壓力如何,漏桶算法總是以固定的流速處理數據,漏桶的容積和流出速率是該算法的兩個重要參數。

令牌桶算法是一種反向的漏桶算法。在令牌桶算法中,桶中存放的不再是請求,而是令牌,處理程序只有拿到令牌後,才能對請求進行處理,如果沒有令牌,要麼等待要麼丟棄。爲了限制流速,該算法在每個單位時間內產生一定量的令牌存放在桶中。比如,要限定應用每秒只能處理1個請求,那麼令牌桶就會每秒產生1個令牌。

而RateLimiter則採用了令牌桶算法,下面演示RateLimiter的使用方法。

public class Main   {
    static RateLimiter rateLimiter  =RateLimiter.create(2);

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            rateLimiter.acquire();
            new Thread(new Test()).start();
        }
    }
    public static class Test implements Runnable{
        @Override
        public void run() {
            System.out.println(System.currentTimeMillis());
        }
    }
}

上面代碼限制了RateLimiter每秒只能處理兩個請求,使用acquire()方法來控制流量,輸出如下,從時間戳可以看到每秒最多輸出兩條記錄,起到了流量控制的效果。

1587884549085
1587884549588
1587884550084
1587884550584
1587884551084
1587884551584
1587884552084
1587884552584
1587884553085
1587884553584

在調用acquire()方法時,過剩的流量會等待,直到有機會執行,而有時候我們需要丟棄過載請求,則可以使用tryAcquire()方法,如下所示。

public class Main   {
    static RateLimiter rateLimiter  =RateLimiter.create(2);
    public static void main(String[] args) {
        for (int i = 0; i < 50; i++) {
            if (!rateLimiter.tryAcquire()){
              continue;
            }
            new Thread(new Test()).start();
        }
    }
    public static class Test implements Runnable{
        @Override
        public void run() {
            System.out.println(System.currentTimeMillis());
        }
    }
}

當請求成功時,tryAcquire()返回true,否則返回false,該方法不會阻塞。上述代碼中,如果數據了超過限制,則超出部分直接丟棄,不在進行處理。

所實例RateLimiter僅支持1秒調用兩次,也就是每500毫秒可以產生一個令牌,但是由於for循環本身效率很高,可以在500毫秒內完成,所以本段代碼最終只產生1個輸出、其餘請求全部被丟棄。
在這裏插入圖片描述

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