以下爲使用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代碼如下:
|
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代碼如下:
|
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代碼如下:
|
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代碼如下:
|
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,代碼如下:
|
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,代碼如下:
|
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時設置每秒產生的許可數量是較小或較大,無論在獲取許可時使用哪種方法,當獲取許可時,均可立刻獲取到。
許可生成及獲取信息如下:
每秒生成許可數量 |
生成許可方式 |
每次獲得許可數量 |
獲取許可等待時間 |
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。