以下为使用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。