1. 概述
本文将介绍 Guava 库中的 RateLimiter
类。这个类用于控制操作执行的速率,是处理流量限制的利器。当我们创建一个 RateLimiter
并设置 N 个许可时,意味着每秒最多能处理 N 个许可的请求。
2. Maven 依赖
首先添加 Guava 依赖(最新版本可在 Maven 仓库 查询):
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.3-jre</version>
</dependency>
3. 创建和使用 RateLimiter
假设我们需要将 doSomeLimitedOperation()
方法的执行速率限制为每秒 2 次。通过 create()
工厂方法创建 RateLimiter
实例:
RateLimiter rateLimiter = RateLimiter.create(2);
获取执行许可需调用 acquire()
方法:
rateLimiter.acquire(1);
验证效果:连续调用两次受限方法,并记录耗时:
long startTime = ZonedDateTime.now().getSecond();
rateLimiter.acquire(1);
doSomeLimitedOperation();
rateLimiter.acquire(1);
doSomeLimitedOperation();
long elapsedTimeSeconds = ZonedDateTime.now().getSecond() - startTime;
假设 doSomeLimitedOperation()
立即完成,两次 acquire()
调用应无阻塞,耗时应在 1 秒内:
assertThat(elapsedTimeSeconds <= 1);
✅ 关键点:也可一次性获取多个许可,例如发送 100 字节/秒的场景:
@Test
public void givenLimitedResource_whenRequestOnce_thenShouldPermitWithoutBlocking() {
// given
RateLimiter rateLimiter = RateLimiter.create(100);
// when
long startTime = ZonedDateTime.now().getSecond();
rateLimiter.acquire(100); // 一次性获取100许可
doSomeLimitedOperation();
long elapsedTimeSeconds = ZonedDateTime.now().getSecond() - startTime;
// then
assertThat(elapsedTimeSeconds <= 1);
}
4. 阻塞式获取许可
考虑更复杂场景:创建 100 许可/秒的限流器,执行需要 1000 许可的操作。根据设计,这至少需要 10 秒完成:
@Test
public void givenLimitedResource_whenUseRateLimiter_thenShouldLimitPermits() {
// given
RateLimiter rateLimiter = RateLimiter.create(100);
// when
long startTime = ZonedDateTime.now().getSecond();
IntStream.range(0, 1000).forEach(i -> {
rateLimiter.acquire(); // 每次获取1许可
doSomeLimitedOperation();
});
long elapsedTimeSeconds = ZonedDateTime.now().getSecond() - startTime;
// then
assertThat(elapsedTimeSeconds >= 10);
}
⚠️ 注意:acquire()
是阻塞方法!调用时会阻塞线程直到获取许可。无参数调用等价于 acquire(1)
。
5. 带超时的许可获取
RateLimiter
提供了带超时的 tryAcquire()
方法,避免无限期阻塞:
@Test
public void givenLimitedResource_whenTryAcquire_shouldNotBlockIndefinitely() {
// given
RateLimiter rateLimiter = RateLimiter.create(1);
// when
rateLimiter.acquire(); // 消耗唯一许可
boolean result = rateLimiter.tryAcquire(2, 10, TimeUnit.MILLISECONDS); // 尝试获取2个许可
// then
assertThat(result).isFalse(); // 必然失败
}
✅ 关键特性:
- 超时时间内获取到许可 → 返回
true
- 超时未获取到许可 → 立即返回
false
- 适用于需要快速响应的场景
6. 总结
本文深入探讨了 Guava RateLimiter
的核心用法:
- 创建限流器:通过
create()
指定许可速率 - 基本限流:使用
acquire()
进行阻塞式许可获取 - 超时控制:通过
tryAcquire()
避免无限等待 - 灵活场景:支持一次性获取多个许可
⚠️ 踩坑提醒:阻塞式 acquire()
在高并发场景可能导致线程堆积,建议优先使用带超时的 tryAcquire()
。
所有示例代码可在 GitHub 项目 中获取(Maven 项目可直接运行)。