guava RateLimiter限流使用總結

以下爲使用guava RateLimiter進行限流的總結,說明了不同模式的區別,以及避免支持併發時導致突發請求支持的tps超過預設的值。

1.  涉及的組件版本

com.google.guava:guava:20.0

2.  google文檔說明

google文檔中RateLimiter類的API地址爲https://google.github.io/guava/releases/20.0/api/docs/index.html?com/google/common/util/concurrent/RateLimiter.html

RateLimiter可以以可配置的速率分配許可。每個acquire請求都都會被阻塞,直到有可用許可後。當許可被獲取後,不需要被釋放。

RateLimiter可用於限流,使交易以指定的速率執行。

2.1  RateLimiter創建

RateLimiter包含以下創建方法:

create(double permitsPerSecond)

create(double permitsPerSecond, long warmupPeriod, TimeUnit unit)

2.1.1  create(double permitsPerSecond)

創建指定的穩定吞吐量的RateLimiter對象,可以指定每秒允許的許可,通常被稱爲QPS,每秒查詢數。

返回的RateLimiter確保在任意秒內創建的許可平均不超過permitPerSecond,持續的請求會平滑分佈在每一秒。當傳入的請求速率超過permitPerSecond時,速率限制器將每隔(1.0 / permitsPerSecond)秒釋放一個許可。 當速率限制器未被使用時,將允許突發請求最多獲取permitPerSecond個許可,隨後的請求將被平穩地限制在permitPerSecond的穩定速率。

l  permitsPerSecond參數指定返回的RateLimiter的速度,即每秒可用的許可數。

2.1.2  create(double permitsPerSecond, long warmupPeriod, TimeUnit unit)

創建具有指定穩定吞吐量的RateLimiter,可以指定“每秒許可數”(通常稱爲QPS,每秒查詢數)與預熱期爲單位。在預熱期內,RateLimiter平滑地提高其速率,直到最後達到其最大速率爲止(只要有足夠的要求使其飽和)。 類似地,如果RateLimiter在warmupPeriod的持續時間內未使用,它將逐漸返回其“冷”狀態,即它將經歷與首次創建時相同的預熱過程。

返回的RateLimiter適用於以下情況:請求的資源(例如,遠程服務器)需要“預熱”時間而不是以穩定(最大)速率立即訪問。

返回的RateLimiter以“冷”狀態開始(即預熱期跟隨在其後),如果它未被使用足夠長時間,它將返回到該狀態。

l  permitsPerSecond參數指定返回的RateLimiter的速度,即每秒可用的許可數。

l  warmupPeriod 參數指定在達到穩定(最大)速度前,RateLimiter提升速度的持續時期。

2.2  RateLimiter獲取許可

RateLimiter包含以下獲取許可方法:

acquire()

acquire(int permits)

tryAcquire()

tryAcquire(int permits)

tryAcquire(int permits, long timeout, TimeUnit unit)

tryAcquire(long timeout, TimeUnit unit)

l  acquire()

從當前RateLimiter請求1個許可,阻塞直到當前請求獲得許可,返回等待的時間,該方法等同於acquire(1)。

l  acquire(int permits)

從當前RateLimiter請求permits個許可,阻塞直到當前請求獲得許可,返回等待的時間。

l  tryAcquire()

從當前RateLimiter請求1個許可,如果能夠立即獲得許可,該方法會返回true;如果不能立即獲得許可,返回false,該方法等同於tryAcquire(1)。

l  tryAcquire(int permits)

從當前RateLimiter請求permits個許可,如果能夠立即獲得許可,該方法會返回true;如果不能立即獲得許可,返回false。

l  tryAcquire(int permits, long timeout, TimeUnit unit)

從當前RateLimiter請求permits個許可,如果能夠在timeout的時間內獲得許可,該方法會返回true;如果不能在timeout的時間內獲得許可,返回false。

l  tryAcquire(long timeout, TimeUnit unit)

從當前RateLimiter請求1個許可,如果能夠在timeout的時間內獲得許可,該方法會返回true;如果不能在timeout的時間內獲得許可,返回false,該方法等同於tryAcquire(1, timeout, unit)。

3.  RateLimiter代碼簡單分析

3.1  RateLimiter類繼承關係

Object

└RateLimiter

  └SmoothRateLimiter

    ├SmoothWarmingUp in SmoothRateLimiter

    └SmoothBursty in SmoothRateLimiter

 

3.2  RateLimiter創建

3.2.1  create(double permitsPerSecond)

create(double permitsPerSecond)方法調用了create(SleepingStopwatch stopwatch, double permitsPerSecond)方法,在其中創建了SmoothRateLimiter$SmoothBursty類的實例並返回。

SmoothRateLimiter$SmoothBursty的吞吐量是穩定的,沒有預熱期。

3.2.2  create(double permitsPerSecond, long warmupPeriod, TimeUnit unit)

create(double permitsPerSecond, long warmupPeriod, TimeUnit unit)方法調用了create(SleepingStopwatch stopwatch, double permitsPerSecond, long warmupPeriod, TimeUnit unit, double coldFactor)方法,在其中創建了SmoothRateLimiter$SmoothWarmingUp類的實例並返回。

SmoothRateLimiter$SmoothWarmingUp吞吐量有預熱期,需要等待預熱期後達到最大速率。

3.3  RateLimiter獲取許可

3.3.1  acquire()

同acquire(1)。

3.3.2  acquire(int permits)

該方法中調用了stopwatch.sleepMicrosUninterruptibly,會進行等待。

3.3.3  tryAcquire()

同tryAcquire(1)。

3.3.4  tryAcquire(int permits)

該方法調用了tryAcquire(int permits, long timeout, TimeUnit unit)方法,傳入的timeout參數爲0,即不會進行等待。

3.3.5  tryAcquire(int permits, long timeout, TimeUnit unit)

當調用canAcquire方法false時,代表無法獲取到許可,會立即返回false;

之後調用了stopwatch.sleepMicrosUninterruptibly,會進行等待。

3.3.6  tryAcquire(long timeout, TimeUnit unit)

同tryAcquire(1, timeout, unit)。

3.4  How is the RateLimiter designed, and why?

在com.google.common.util.concurrent.SmoothRateLimiter類的註解中包含“How is the RateLimiter designed, and why?”的說明,內容較長。

4.  RateLimiter使用測試

以下測試用於觀察RateLimiter使用不同的創建方式,與不同的獲取令牌方式時,表現的特性有何區別。

4.1  每秒允許的許可數大於1(預熱期非0)

以下測試中,創建的RateLimiter支持的每秒允許的許可數爲5,即每秒生成5個許可,每經過約200毫秒生成1個許可。

4.1.1  測試用例

在進行測試時,共測試11批次,每批對獲取許可方法調用指定次數,再等待1秒(若當前批次執行耗時超過1秒,則不等待),使下個批次獲取許可時已進入下一週期。再等待指定的時間(可能爲0),觀察等待多個週期後再獲取許可的效果。

測試用例如下:

測試批次

獲取許可次數

當前批次結束後等待時間

1

5

1秒

2

5

1秒

3

10

1秒

4

10

1秒+5秒

5

10

1秒

6

5

1秒

7

10

1秒+10秒

8

10

1秒

9

10

1秒

10

10

1秒+10秒

11

20

不等待

4.1.2  支持突發

調用RateLimiter.create(double permitsPerSecond)方法創建的RateLimiter支持突發,不需要預熱期即可達到穩定(最大)的許可生成速率。

生成RateLimiter代碼如下:

RateLimiter rateLimiter = RateLimiter.create(5.0D);

4.1.2.1     獲取許可不等待

以下測試調用RateLimiter.tryAcquire(1)方法獲取許可時,每次獲取1個許可,不等待,若獲取不到許可,會立刻返回false。

許可生成及獲取信息如下:

每秒生成許可數量

生成許可方式

每次獲得許可數量

獲取許可等待時間

5

支持突發

1

不等待

每個批次執行結果如下:

執行批次

執行次數

獲得許可成功次數

消耗時間

開始前等待時間

每秒獲得許可成功次數

1

5

1

0ms

0.00s(0ms)

1

2

5

5

16ms

0.98s(984ms)

5

3

10

5

0ms

1.00s(1000ms)

5

4

10

5

0ms

1.00s(1000ms)

5

5

10

6

0ms

6.00s(6000ms)

6

6

5

5

0ms

1.00s(1000ms)

5

7

10

5

0ms

1.00s(1000ms)

5

8

10

6

0ms

11.00s(11000ms)

6

9

10

5

0ms

1.00s(1000ms)

5

10

10

5

0ms

1.00s(1000ms)

5

11

20

6

0ms

11.00s(11000ms)

6

第1批執行結果如下:

13:07:35.708 [第1批] [開始 執行次數: 5]

13:07:35.708 [第1批,第1次] 獲取到許可

13:07:35.708 [第1批,第2次] 未獲取到許可

13:07:35.708 [第1批,第3次] 未獲取到許可

13:07:35.708 [第1批,第4次] 未獲取到許可

13:07:35.708 [第1批,第5次] 未獲取到許可

在RateLimiter創建後第一次執行時,第1秒內僅第1筆交易獲取到許可,可能與RateLimiter初始化剛完成有關。

使用相同的條件反覆執行多次,第2批可能獲取到4個許可,也可能獲取到5個許可,可能與RateLimiter初始化剛完成有關。

當等待一段時間後再獲取許可時,第一秒內可以獲取到6個許可,見第5、8、11批次的結果。

4.1.2.2     獲取許可等待時間小於許可生成間隔

以下測試調用RateLimiter.tryAcquire(1, 100L, TimeUnit.MILLISECONDS)方法獲取許可時,每次獲取1個許可,最多等待100毫秒,若在100毫秒內獲取不到許可,會返回false。

許可生成及獲取信息如下:

每秒生成許可數量

生成許可方式

每次獲得許可數量

獲取許可等待時間

5

支持突發

1

最多等待100毫秒

每個批次執行結果如下:

執行批次

執行次數

獲得許可成功次數

消耗時間

開始前等待時間

每秒獲得許可成功次數

1

5

1

0ms

0.00s(0ms)

1.00

2

5

5

0ms

1.00s(1000ms)

5.00

3

10

5

0ms

1.00s(1000ms)

5.00

4

10

5

0ms

1.00s(1000ms)

5.00

5

10

6

0ms

6.00s(6000ms)

6.00

6

5

5

0ms

1.00s(1000ms)

5.00

7

10

5

0ms

1.00s(1000ms)

5.00

8

10

6

0ms

11.00s(11000ms)

6.00

9

10

5

0ms

1.00s(1000ms)

5.00

10

10

5

0ms

1.00s(1000ms)

5.00

11

20

6

0ms

11.00s(11000ms)

6.00

每次獲取1個許可,最多等待100毫秒,由於當前RateLimiter生成1個許可需要200毫秒,獲取許可等待時間小於許可生成間隔,因此與獲取許可時不等待執行結果類似。

4.1.2.3     獲取許可等待時間大於許可生成間隔

以下測試調用RateLimiter.tryAcquire(1, 300L, TimeUnit.MILLISECONDS)方法獲取許可時,每次獲取1個許可,最多等待300毫秒,若在300毫秒內獲取不到許可,會返回false。

許可生成及獲取信息如下:

每秒生成許可數量

生成許可方式

每次獲得許可數量

獲取許可等待時間

5

支持突發

1

最多等待300毫秒

每個批次執行結果如下:

執行批次

執行次數

獲得許可成功次數

消耗時間

開始前等待時間

每秒獲得許可成功次數

1

5

5

785ms

0.00s(0ms)

5.00

2

5

5

782ms

1.00s(1000ms)

5.00

3

10

10

1746ms

1.00s(1000ms)

5.73

4

10

10

1963ms

1.00s(1000ms)

5.09

5

10

10

784ms

6.00s(6000ms)

10.00

6

5

5

769ms

1.00s(1000ms)

5.00

7

10

10

1751ms

1.00s(1000ms)

5.71

8

10

10

784ms

11.00s(11000ms)

10.00

9

10

10

1767ms

1.00s(1000ms)

5.66

10

10

10

1994ms

1.00s(1000ms)

5.02

11

20

20

2781ms

11.00s(11000ms)

7.19

第1、5、11批次執行結果如下:

11:38:45.220 [第1批] [開始 執行次數: 5]

11:38:45.235 [第1批,第1次] 獲取到許可 獲取許可等待時間:15ms

11:38:45.414 [第1批,第2次] 獲取到許可 獲取許可等待時間:179ms

11:38:45.633 [第1批,第3次] 獲取到許可 獲取許可等待時間:219ms

11:38:45.805 [第1批,第4次] 獲取到許可 獲取許可等待時間:172ms

11:38:46.005 [第1批,第5次] 獲取到許可 獲取許可等待時間:200ms

11:38:54.285 [第5批] [開始 執行次數: 10]

11:38:54.285 [第5批,第1次] 獲取到許可 獲取許可等待時間:0ms

11:38:54.285 [第5批,第2次] 獲取到許可 獲取許可等待時間:0ms

11:38:54.285 [第5批,第3次] 獲取到許可 獲取許可等待時間:0ms

11:38:54.285 [第5批,第4次] 獲取到許可 獲取許可等待時間:0ms

11:38:54.285 [第5批,第5次] 獲取到許可 獲取許可等待時間:0ms

11:38:54.285 [第5批,第6次] 獲取到許可 獲取許可等待時間:0ms

11:38:54.470 [第5批,第7次] 獲取到許可 獲取許可等待時間:185ms

11:38:54.689 [第5批,第8次] 獲取到許可 獲取許可等待時間:219ms

11:38:54.869 [第5批,第9次] 獲取到許可 獲取許可等待時間:180ms

11:38:55.069 [第5批,第10次] 獲取到許可 獲取許可等待時間:200ms

13:02:34.195 [第11批] [開始 執行次數: 20]

13:02:34.195 [第11批,第1次] 獲取到許可 獲取許可等待時間:0ms

13:02:34.195 [第11批,第2次] 獲取到許可 獲取許可等待時間:0ms

13:02:34.195 [第11批,第3次] 獲取到許可 獲取許可等待時間:0ms

13:02:34.195 [第11批,第4次] 獲取到許可 獲取許可等待時間:0ms

13:02:34.195 [第11批,第5次] 獲取到許可 獲取許可等待時間:0ms

13:02:34.195 [第11批,第6次] 獲取到許可 獲取許可等待時間:0ms

13:02:34.380 [第11批,第7次] 獲取到許可 獲取許可等待時間:185ms

13:02:34.581 [第11批,第8次] 獲取到許可 獲取許可等待時間:201ms

13:02:34.780 [第11批,第9次] 獲取到許可 獲取許可等待時間:199ms

13:02:34.978 [第11批,第10次] 獲取到許可 獲取許可等待時間:198ms

13:02:35.178 [第11批,第11次] 獲取到許可 獲取許可等待時間:200ms

13:02:35.377 [第11批,第12次] 獲取到許可 獲取許可等待時間:199ms

13:02:35.576 [第11批,第13次] 獲取到許可 獲取許可等待時間:199ms

13:02:35.775 [第11批,第14次] 獲取到許可 獲取許可等待時間:199ms

13:02:35.973 [第11批,第15次] 獲取到許可 獲取許可等待時間:198ms

13:02:36.173 [第11批,第16次] 獲取到許可 獲取許可等待時間:200ms

13:02:36.372 [第11批,第17次] 獲取到許可 獲取許可等待時間:199ms

13:02:36.571 [第11批,第18次] 獲取到許可 獲取許可等待時間:199ms

13:02:36.770 [第11批,第19次] 獲取到許可 獲取許可等待時間:199ms

13:02:36.969 [第11批,第20次] 獲取到許可 獲取許可等待時間:199ms

每次獲取1個許可,最多等待300毫秒,由於當前RateLimiter生成1個許可需要200毫秒,獲取許可等待時間大於許可生成間隔,因此每個請求都獲取到了許可。

在RateLimiter創建後第一次執行時,第1秒內獲取了5個許可,未出現獲取許可不等待時第1秒內僅獲取1個許可的情況。

當持續獲取許可時,每次獲取許可需等待約200毫秒。

當等待一段時間後再獲取許可時,第1秒內最多可獲取10個許可。在獲取前5~6個許可時,不需要等待,可立刻獲取;在獲取之後的許可時,每次等待約200毫秒。見第5、8、11批次的執行結果。

4.1.2.4     等待直到獲得許可

以下測試調用RateLimiter.acquire(1)方法獲取許可時,每次獲取1個許可,等待直到獲得許可。

許可生成及獲取信息如下:

每秒生成許可數量

生成許可方式

每次獲得許可數量

獲取許可等待時間

5

支持突發

1

等待直到獲得許可

每個批次執行結果如下:

執行批次

執行次數

獲得許可成功次數

消耗時間

開始前等待時間

每秒獲得許可成功次數

1

5

5

781ms

0.00s(0ms)

5.00

2

5

5

760ms

1.00s(1000ms)

5.00

3

10

10

1739ms

1.00s(1000ms)

5.75

4

10

10

1991ms

1.00s(1000ms)

5.02

5

10

10

798ms

6.00s(6000ms)

10.00

6

5

5

768ms

1.00s(1000ms)

5.00

7

10

10

1747ms

1.00s(1000ms)

5.72

8

10

10

783ms

11.00s(11000ms)

10.00

9

10

10

1763ms

1.00s(1000ms)

5.67

10

10

10

1996ms

1.00s(1000ms)

5.01

11

20

20

2760ms

11.00s(11000ms)

7.25

等待直到獲得許可,與獲取許可等待時間大於許可生成間隔時的執行結果類似。

4.1.3  預熱期爲1毫秒

調用RateLimiter.create(double permitsPerSecond, long warmupPeriod, TimeUnit unit)方法創建的RateLimiter需要經過預熱期後纔可達到穩定(最大)的許可生成速率。

當前用於測試的RateLimiter每秒生成5個許可,即每生成1個許可需要約200毫秒,在創建時將預熱期設置爲1毫秒。

生成RateLimiter代碼如下:

RateLimiter.create(5.0D, 1L, TimeUnit.MILLISECONDS);

4.1.3.1     獲取許可等待時間較短

以下測試調用RateLimiter.tryAcquire(1, 100L, TimeUnit.MILLISECONDS)方法獲取許可時,每次獲取1個許可,最多等待100毫秒。

許可生成及獲取信息如下:

每秒生成許可數量

生成許可方式

每次獲得許可數量

獲取許可等待時間

5

預熱期爲1毫秒

1

最多等待100毫秒

每個批次執行結果如下:

執行批次

執行次數

獲得許可成功次數

消耗時間

開始前等待時間

每秒獲得許可成功次數

1

5

1

0ms

0.00s(0ms)

1.00

2

5

1

0ms

1.00s(1000ms)

1.00

3

10

1

0ms

1.00s(1000ms)

1.00

4

10

1

0ms

1.00s(1000ms)

1.00

5

10

1

0ms

6.00s(6000ms)

1.00

6

5

1

0ms

1.00s(1000ms)

1.00

7

10

1

0ms

1.00s(1000ms)

1.00

8

10

1

0ms

11.00s(11000ms)

1.00

9

10

1

0ms

1.00s(1000ms)

1.00

10

10

1

0ms

1.00s(1000ms)

1.00

11

20

1

0ms

11.00s(11000ms)

1.00

每次獲取1個許可,最多等待100毫秒,由於等待時間較短,RateLimiter生成許可的速率還未達到穩定(最大),每個時間週期(1秒)內都只能獲取1個許可。

4.1.3.2     獲取許可等待時間足夠長

以下測試調用RateLimiter.acquire(1)方法獲取許可時,每次獲取1個許可,等待直到獲得許可。

許可生成及獲取信息如下:

每秒生成許可數量

生成許可方式

每次獲得許可數量

獲取許可等待時間

5

預熱期爲200毫秒

1

等待直到獲得許可

每個批次執行結果如下:

執行批次

執行次數

獲得許可成功次數

消耗時間

開始前等待時間

每秒獲得許可成功次數

1

5

5

1102ms

0.00s(0ms)

4.54

2

5

5

781ms

1.00s(1000ms)

5.00

3

10

10

1852ms

1.00s(1000ms)

5.40

4

10

10

1991ms

1.00s(1000ms)

5.02

5

10

10

1767ms

6.00s(6000ms)

5.66

6

5

5

995ms

1.00s(1000ms)

5.00

7

10

10

1983ms

1.00s(1000ms)

5.04

8

10

10

1778ms

11.00s(11000ms)

5.62

9

10

10

1991ms

1.00s(1000ms)

5.02

10

10

10

1982ms

1.00s(1000ms)

5.05

11

20

20

3769ms

11.00s(11000ms)

5.31

每次獲取1個許可,等待直到獲得許可,每個請求都獲取到了許可。

當等待一段時間後再獲取許可時,第1秒內獲得許可約5個。

4.1.4  預熱期等於許可生成間隔

當前用於測試的RateLimiter每秒生成5個許可,即每生成1個許可需要約200毫秒,在創建時將預熱期設置爲200毫秒,與許可生成時間間隔相同。

生成RateLimiter代碼如下:

RateLimiter.create(5.0D, 200L, TimeUnit.MILLISECONDS);

4.1.4.1     獲取許可等待時間較短

以下測試調用RateLimiter.tryAcquire(1, 100L, TimeUnit.MILLISECONDS)方法獲取許可時,每次獲取1個許可,最多等待100毫秒。

許可生成及獲取信息如下:

每秒生成許可數量

生成許可方式

每次獲得許可數量

獲取許可等待時間

5

預熱期爲200毫秒

1

最多等待100毫秒

每個批次執行結果如下:

執行批次

執行次數

獲得許可成功次數

消耗時間

開始前等待時間

每秒獲得許可成功次數

1

5

1

0ms

0.00s(0ms)

1.00

2

5

1

0ms

1.00s(1000ms)

1.00

3

10

1

0ms

1.00s(1000ms)

1.00

4

10

1

0ms

1.00s(1000ms)

1.00

5

10

1

0ms

6.00s(6000ms)

1.00

6

5

1

0ms

1.00s(1000ms)

1.00

7

10

1

0ms

1.00s(1000ms)

1.00

8

10

1

0ms

11.00s(11000ms)

1.00

9

10

1

0ms

1.00s(1000ms)

1.00

10

10

1

0ms

1.00s(1000ms)

1.00

11

20

1

0ms

11.00s(11000ms)

1.00

每次獲取1個許可,最多等待100毫秒,由於等待時間較短,RateLimiter生成許可的速率還未達到穩定(最大),每個時間週期(1秒)內都只能獲取1個許可。

與生成RateLimiter預熱期爲1毫秒,獲取許可等待時間較短時的執行結果類似。

4.1.4.2     獲取許可等待時間足夠長

以下測試調用RateLimiter.acquire(1)方法獲取許可時,每次獲取1個許可,等待直到獲得許可。

許可生成及獲取信息如下:

每秒生成許可數量

生成許可方式

每次獲得許可數量

獲取許可等待時間

5

預熱期爲200毫秒

1

等待直到獲得許可

每個批次執行結果如下:

執行批次

執行次數

獲得許可成功次數

消耗時間

開始前等待時間

每秒獲得許可成功次數

1

5

5

887ms

0.00s(0ms)

5.00

2

5

5

912ms

0.99s(985ms)

5.00

3

10

10

1879ms

1.00s(1000ms)

5.32

4

10

10

1986ms

1.00s(1000ms)

5.04

5

10

10

1873ms

6.00s(6000ms)

5.34

6

5

5

976ms

1.00s(1000ms)

5.00

7

10

10

1991ms

1.00s(1000ms)

5.02

8

10

10

1879ms

11.00s(11000ms)

5.32

9

10

10

1993ms

1.00s(1000ms)

5.02

10

10

10

1988ms

1.00s(1000ms)

5.03

11

20

20

3869ms

11.00s(11000ms)

5.17

每次獲取1個許可,等待直到獲得許可,每個請求都獲取到了許可。

當等待一段時間後再獲取許可時,第1秒內獲得許可約5個。

與生成RateLimiter預熱期爲1毫秒,獲取許可等待時間足夠長的執行結果類似。

4.1.5  預熱期大於許可生成間隔

當前用於測試的RateLimiter每秒生成5個許可,即每生成1個許可需要約200毫秒,在創建時將預熱期設置爲10000毫秒,大於許可生成時間間隔。

生成RateLimiter代碼如下:

RateLimiter.create(5.0D, 10000L, TimeUnit.MILLISECONDS);

4.1.5.1     獲取許可等待時間較短

以下測試調用RateLimiter.tryAcquire(1, 100L, TimeUnit.MILLISECONDS)方法獲取許可時,每次獲取1個許可,最多等待100毫秒。

許可生成及獲取信息如下:

每秒生成許可數量

生成許可方式

每次獲得許可數量

獲取許可等待時間

5

預熱期爲10000毫秒

1

最多等待100毫秒

每個批次執行結果如下:

執行批次

執行次數

獲得許可成功次數

消耗時間

開始前等待時間

每秒獲得許可成功次數

1

5

1

0ms

0.00s(0ms)

1.00

2

5

1

0ms

1.00s(1000ms)

1.00

3

10

1

0ms

1.00s(1000ms)

1.00

4

10

1

0ms

1.00s(1000ms)

1.00

5

10

1

0ms

6.00s(6000ms)

1.00

6

5

1

0ms

1.00s(1000ms)

1.00

7

10

1

0ms

1.00s(1000ms)

1.00

8

10

1

0ms

11.00s(11000ms)

1.00

9

10

1

0ms

1.00s(1000ms)

1.00

10

10

1

0ms

1.00s(1000ms)

1.00

11

20

1

0ms

11.00s(11000ms)

1.00

每次獲取1個許可,最多等待100毫秒,由於等待時間較短,RateLimiter生成許可的速率還未達到穩定(最大),每個時間週期(1秒)內都只能獲取1個許可。

與生成RateLimiter預熱期爲1毫秒,獲取許可等待時間較短時的執行結果類似。

4.1.5.2     獲取許可等待時間足夠長

以下測試調用RateLimiter.acquire(1)方法獲取許可時,每次獲取1個許可,等待直到獲得許可。

許可生成及獲取信息如下:

每秒生成許可數量

生成許可方式

每次獲得許可數量

獲取許可等待時間

5

預熱期爲10000毫秒

1

等待直到獲得許可

每個批次執行結果如下:

執行批次

執行次數

獲得許可成功次數

消耗時間

開始前等待時間

每秒獲得許可成功次數

s1

5

5

2268ms

0.00s(0ms)

2.20

2

5

5

2464ms

1.00s(1000ms)

2.03

3

10

10

3735ms

1.00s(1000ms)

2.68

4

10

10

2257ms

1.00s(1000ms)

4.43

5

10

10

1976ms

1.00s(1000ms)

5.06

6

5

5

995ms

1.00s(1000ms)

5.00

7

10

10

1991ms

1.00s(1000ms)

5.02

8

10

10

2028ms

11.00s(11000ms)

4.93

9

10

10

1990ms

1.00s(1000ms)

5.03

10

10

10

1991ms

1.00s(1000ms)

5.02

11

20

20

4503ms

11.00s(11000ms)

4.44

每次獲取1個許可,等待直到獲得許可,在預熱期(前10秒)內,每秒獲取許可數少於5個;在預熱期結束後(10秒後),每秒獲取許可數約5個。

當等待一段時間後再獲取許可時,第1秒內獲得許可約5個。

RateLimiter.acquire(1)方法獲取許可時的前一部分日誌如下。可以看到開始獲取許可後,到第10秒後獲取許可達到固定(最大)速率,約200毫秒獲取一個許可。在前10秒的預熱期內,獲取許可速度較慢,之後逐步變快;到預熱期結束後,獲取許可速率達到固定(最大)值。

14:12:37.604 [第1批,第1次] 獲取許可等待時間: 0.00ms

14:12:38.198 [第1批,第2次] 獲取許可等待時間: 590.06ms

14:12:38.810 [第1批,第3次] 獲取許可等待時間: 576.91ms

14:12:39.328 [第1批,第4次] 獲取許可等待時間: 517.17ms

14:12:39.872 [第1批,第5次] 獲取許可等待時間: 542.70ms

14:12:40.398 [第2批,第1次] 獲取許可等待時間: 524.88ms

14:12:40.914 [第2批,第2次] 獲取許可等待時間: 499.69ms

14:12:41.414 [第2批,第3次] 獲取許可等待時間: 489.84ms

14:12:41.898 [第2批,第4次] 獲取許可等待時間: 469.89ms

14:12:42.351 [第2批,第5次] 獲取許可等待時間: 449.54ms

14:12:42.781 [第3批,第1次] 獲取許可等待時間: 443.97ms

14:12:43.214 [第3批,第2次] 獲取許可等待時間: 431.96ms

14:12:43.628 [第3批,第3次] 獲取許可等待時間: 414.35ms

14:12:44.028 [第3批,第4次] 獲取許可等待時間: 399.37ms

14:12:44.411 [第3批,第5次] 獲取許可等待時間: 382.98ms

14:12:44.778 [第3批,第6次] 獲取許可等待時間: 366.19ms

14:12:45.129 [第3批,第7次] 獲取許可等待時間: 350.88ms

14:12:45.464 [第3批,第8次] 獲取許可等待時間: 334.46ms

14:12:45.784 [第3批,第9次] 獲取許可等待時間: 318.53ms

14:12:46.086 [第3批,第10次] 獲取許可等待時間: 302.18ms

14:12:46.446 [第4批,第1次] 獲取許可等待時間: 285.85ms

14:12:46.638 [第4批,第2次] 獲取許可等待時間: 192.28ms

14:12:46.894 [第4批,第3次] 獲取許可等待時間: 254.93ms

14:12:47.132 [第4批,第4次] 獲取許可等待時間: 238.06ms

14:12:47.356 [第4批,第5次] 獲取許可等待時間: 222.87ms

14:12:47.562 [第4批,第6次] 獲取許可等待時間: 206.18ms

14:12:47.761 [第4批,第7次] 獲取許可等待時間: 199.20ms

14:12:47.961 [第4批,第8次] 獲取許可等待時間: 198.93ms

14:12:48.160 [第4批,第9次] 獲取許可等待時間: 198.77ms

14:12:48.359 [第4批,第10次] 獲取許可等待時間: 198.52ms

4.2  每秒允許的許可數小於1(預熱期非0)

當需要使RateLimiter每秒允許的許可數小於1時,可通過兩種方法,一是生成RateLimiter時設置每秒允許的許可數小於1,二是從RateLimiter獲取許可時獲取多個。

以下測試中,使用的RateLimiter均爲支持突發,支持每秒允許的許可數爲0.1,即每秒生成0.1個許可,每經過約10秒生成1個許可。

4.2.1  測試用例

在進行測試時,共測試5批次,每批對獲取許可方法調用指定次數,再等待1秒(若當前批次執行耗時超過1秒,則不等待),使下個批次獲取許可時已進入下一週期。再等待指定的時間(可能爲0),觀察等待多個週期後再獲取許可的效果。

測試用例如下:

測試批次

獲取許可次數

當前批次結束後等待時間

1

2

1秒

2

2

1秒

3

2

1秒+4秒

4

2

1秒+4秒

5

2

1秒10秒

6

2

不等待

4.2.2  生成RateLimiter時設置每秒允許的許可數小於1

調用RateLimiter.create(double permitsPerSecond)方法創建支持突發的RateLimiter。

生成每秒允許0.1個許可的RateLimiter,代碼如下:

RateLimiter rateLimiter = RateLimiter.create(0.1D);

4.2.2.1     獲取許可不等待

以下測試調用RateLimiter.tryAcquire(1)方法獲取許可時,每次獲取1個許可,不等待,若獲取不到許可,會立刻返回false。

許可生成及獲取信息如下:

每秒生成許可數量

生成許可方式

每次獲得許可數量

獲取許可等待時間

0.1

支持突發

1

不等待

每個批次執行結果如下:

執行批次

執行次數

獲得許可成功次數

消耗時間

開始前等待時間

每秒獲得許可成功次數

1

2

1

0ms

0.00s(0ms)

1.00

2

2

0

0ms

1.00s(1000ms)

0.00

3

2

0

0ms

1.00s(1000ms)

0.00

4

2

0

0ms

5.00s(5000ms)

0.00

5

2

1

0ms

5.00s(5000ms)

1.00

6

2

1

0ms

11.00s(11000ms)

1.00

在RateLimiter創建後第一次執行時可獲取到一個許可,之後需要等待約10秒再獲取一個許可。

4.2.2.2     等待直到獲得許可

以下測試調用RateLimiter.acquire(1)方法獲取許可時,每次獲取1個許可,等待直到獲得許可。

許可生成及獲取信息如下:

每秒生成許可數量

生成許可方式

每次獲得許可數量

獲取許可等待時間

0.1

支持突發

1

等待直到獲得許可

每個批次執行結果如下:

執行批次

執行次數

獲得許可成功次數

消耗時間

開始前等待時間

每秒獲得許可成功次數

1

2

2

9978ms

0.00s(0ms)

0.20

2

2

2

19992ms

0.00s(0ms)

0.10

3

2

2

19976ms

0.00s(0ms)

0.10

4

2

2

15976ms

4.00s(4000ms)

0.13

5

2

2

15976ms

4.00s(4000ms)

0.13

6

2

2

9977ms

10.00s(10000ms)

0.20

第6批次執行結果如下:

21:09:55.468 [test] [第6批] [開始 執行次數: 2]

21:09:55.468 [test] [第6批,第1次] 獲取許可等待時間: 0.00ms

21:10:05.445 [test] [第6批,第2次] 獲取許可等待時間: 9990.56ms

在RateLimiter創建後第一次執行時可獲取到一個許可,之後需要等待約10秒再獲取一個許可。

當等待一段時間後再獲取許可時,第1秒內最多可獲取2個許可。在獲取第1個許可時,不需要等待,可立刻獲取;在獲取之後的許可時,每次等待約10秒。見第6批次的執行結果。

4.2.3  從RateLimiter獲取許可時獲取多個

調用RateLimiter.create(double permitsPerSecond)方法創建支持突發的RateLimiter。

生成每秒允許1個許可的RateLimiter,代碼如下:

RateLimiter rateLimiter = RateLimiter.create(1.0D);

4.2.3.1     獲取許可不等待

以下測試調用RateLimiter.tryAcquire(1)方法獲取許可時,每次獲取10個許可,不等待,若獲取不到許可,會立刻返回false。

許可生成及獲取信息如下:

每秒生成許可數量

生成許可方式

每次獲得許可數量

獲取許可等待時間

1

支持突發

10

不等待

每個批次執行結果如下:

執行批次

執行次數

獲得許可成功次數

消耗時間

開始前等待時間

每秒獲得許可成功次數

1

2

1

0ms

0.00s(0ms)

1.00

2

2

0

0ms

1.00s(1000ms)

0.00

3

2

0

0ms

1.00s(1000ms)

0.00

4

2

0

0ms

5.00s(5000ms)

0.00

5

2

1

0ms

5.00s(5000ms)

1.00

6

2

1

0ms

11.00s(11000ms)

1.00

在RateLimiter創建後第一次執行時可獲取到10個許可,之後需要等待約10秒再獲取10個許可。

4.2.3.2     等待直到獲得許可

以下測試調用RateLimiter.acquire(10)方法獲取許可時,每次獲取10個許可,等待直到獲得許可。

許可生成及獲取信息如下:

每秒生成許可數量

生成許可方式

每次獲得許可數量

獲取許可等待時間

1

支持突發

10

等待直到獲得許可

每個批次執行結果如下:

執行批次

執行次數

獲得許可成功次數

消耗時間

開始前等待時間

每秒獲得許可成功次數

1

2

2

9979ms

0.00s(0ms)

0.20

2

2

2

19992ms

0.00s(0ms)

0.10

3

2

2

20016ms

0.00s(0ms)

0.10

4

2

2

15992ms

4.00s(4000ms)

0.13

5

2

2

15992ms

4.00s(4000ms)

0.13

6

2

2

9977ms

10.00s(10000ms)

0.20

第6批次執行結果如下:

21:39:55.901 [test] [第6批] [開始 執行次數: 2]

21:39:55.901 [test] [第6批,第1次] 獲取許可等待時間: 0.00ms

21:40:05.878 [test] [第6批,第2次] 獲取許可等待時間: 9990.85ms

在RateLimiter創建後第一次執行時可獲取到一個許可,之後需要等待約10秒再獲取一個許可。

當等待一段時間後再獲取許可時,第1秒內最多可獲取20個許可。在獲取前10個許可時,不需要等待,可立刻獲取;在之後獲取10個許可時,每次等待約10秒。見第6批次的執行結果。

4.3  預熱期爲0

在生成RateLimiter時,將預熱期warmupPeriod設置爲0時,產生的RateLimiter可無限獲得許可,無法達到限流的效果。

生成RateLimiter代碼如下:

RateLimiter.create(permitsPerSecond, 0L, TimeUnit.MILLISECONDS);

無論在生成RateLimiter時設置每秒產生的許可數量是較小或較大,無論在獲取許可時使用哪種方法,當獲取許可時,均可立刻獲取到。

許可生成及獲取信息如下:

每秒生成許可數量

生成許可方式

每次獲得許可數量

獲取許可等待時間

0.1

預熱期爲0

1

不等待

執行結果示例如下所示:

執行批次

執行次數

獲得許可成功次數

消耗時間

開始前等待時間

每秒獲得許可成功次數

1

1000000

1000000

1749ms

0.00s(0ms)

571755.29

5.  RateLimiter使用總結

5.1  支持突發創建後立即使用,立即使用第一秒只成功獲取一次許可

使用RateLimiter.create(double permitsPerSecond)方法創建支持突發的RateLimiter,若創建後立即使用,第一秒只能成功獲取一次許可,可能與RateLimiter初始化剛完成有關。

使用時需要注意,儘量將RateLimiter.create方法提前調用,使初始化完成後再獲取許可。

5.2  支持突發,獲取許可時等待,每秒獲取許可數可達最大值兩倍

使用RateLimiter.create(double permitsPerSecond)方法創建支持突發的RateLimiter,獲取許可時等待(獲取許可等待時間大於許可生成間隔,或等待直到獲得許可),等待一段時間後再獲取許可,第一秒內獲取的許可數可以達到最大允許的兩倍。

使用時需要注意,防止實際允許的交易過多,對系統性能造成影響。

5.3  每秒允許許可小於1的使用方法

當需要使每秒允許的許可數小於1時(如每隔n秒/分鐘/小時允許進行一次交易),可使用以下兩種方法實現。

一是生成RateLimiter時設置每秒允許的許可數小於1。調用RateLimiter.create方法創建RateLimiter時,permitsPerSecond設置爲每秒允許的許可數(如每10秒允許1個許可,則設置爲0.1D),在獲取許可時,每次獲取1個許可。

二是從RateLimiter獲取許可時獲取多個。調用RateLimiter.create方法創建RateLimiter時,permitsPerSecond設置爲1.0D,即每秒允許1個許可,在獲取許可時,每次獲取的許可數應等於每個許可生成的時間間隔秒數(如每10秒允許1個許可,則設置爲每次獲取10個許可)。

在使用時需要注意“支持突發,獲取許可時等待,每秒獲取許可數可達最大值兩倍”問題。

5.4  預熱期爲0時限流失效

在生成RateLimiter時,將預熱期warmupPeriod設置爲0時,產生的RateLimiter可無限獲得許可,無法達到限流的效果。

在使用時需要注意,生成RateLimiter時,不能將預熱期warmupPeriod設置爲0。

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