Guava RateLimiter设置每秒10个许可却无法全部获取的问题
Guava RateLimiter 为何设置10QPS却无法全部立即获取令牌?
问题场景
我正在使用Guava 32.1.1版本的RateLimiter,代码如下:
int permitsPerSecond = 10; RateLimiter rateLimiter = RateLimiter.create(permitsPerSecond); Stopwatch stopwatch = Stopwatch.createStarted(); int i = 0; for (; i < permitsPerSecond; i++) { boolean tryAcquired = rateLimiter.tryAcquire(); System.out.println("tryAcquired: " + tryAcquired); } System.out.println("Time elapsed: " + stopwatch.stop().elapsed(TimeUnit.MILLISECONDS)); System.out.println("No. of permit requests processed: " + i);
得到的输出为:
tryAcquired: true tryAcquired: false tryAcquired: false tryAcquired: false tryAcquired: false Time elapsed: 2 No. of permit requests processed: 5
设置permitsPerSecond为10,为何不是所有tryAcquire()调用都返回true?
核心原因
Guava默认的RateLimiter.create(permitsPerSecond)是SmoothBursty实现,它的令牌生成逻辑是匀速增量发放,而非初始化时一次性填满令牌桶,结合tryAcquire()的特性导致了这个结果:
- 令牌匀速生成:10QPS意味着每100毫秒生成1个令牌,令牌不会在RateLimiter初始化时就一次性生成10个存入桶中。
- 无参
tryAcquire()是立即尝试:这个方法等价于tryAcquire(0, TimeUnit.SECONDS),不会等待令牌生成,拿不到就直接返回false。
你的循环在2毫秒内就完成了多次调用,此时除了第一个请求能成功获取令牌(RateLimiter对首次请求做了突发优化,允许立即通过),后续请求的令牌还未生成,所以全部返回false。
调整方案
如果需要让所有请求都能获取令牌,可以根据需求选择:
- 使用
rateLimiter.acquire()替代tryAcquire():这个方法会阻塞等待,直到拿到令牌,10次请求会耗时约1秒(等待所有令牌生成)。 - 初始化时设置突发容量:使用
RateLimiter.create(permitsPerSecond, burstSeconds),指定突发秒数,让令牌桶初始预存permitsPerSecond * burstSeconds个令牌,比如RateLimiter.create(10, 1)会初始生成10个令牌,此时循环的10次tryAcquire()都会返回true。
内容的提问来源于stack exchange,提问作者RRM
相关产品推荐
相关产品推荐

