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。

发布了37 篇原创文章 · 获赞 0 · 访问量 2320

猜你喜欢

转载自blog.csdn.net/a82514921/article/details/104609889