Guava RateLimiter怎么用?从入门到实战的完整指南
目录导读
- 什么是Guava RateLimiter?为什么需要它?
- RateLimiter的核心原理:令牌桶算法与平滑突发
- 环境准备:Maven依赖与基本引入
- 五种常用API详解与代码示例
- 实际场景:秒杀系统、API限流、数据库连接池
- 常见问题FAQ:如何调参?如何处理拒绝?
- 性能对比:RateLimiter vs Semaphore vs 分布式方案
什么是Guava RateLimiter?为什么需要它?
Q:什么是RateLimiter?
A:RateLimiter是Google Guava库提供的一个限流工具,基于令牌桶算法实现,它控制单位时间内允许通过的请求数量,常用于防止系统被突发流量冲垮。

Q:为什么不直接用Semaphore?
A:Semaphore控制的是“并发数”,而RateLimiter控制的是“速率”,Semaphore允许10个线程同时执行,但RateLimiter允许每秒最多100次调用,即使线程空闲也能平滑控制QPS。
RateLimiter的核心原理:令牌桶算法与平滑突发
RateLimiter内部维护一个“令牌桶”,以固定速率向桶中放入令牌,每次请求需要从桶中获取一个或多个令牌,如果桶空则请求被阻塞或拒绝。
关键特性:
- 平滑突发(SmoothBursty):默认模式,允许短时间突发流量(如每秒5个请求,但可缓存前一秒的令牌,允许瞬间10个请求)。
- 平滑预热(SmoothWarmingUp):启动时缓慢增加速率,避免冷启动时系统过载(适用于数据库连接池等场景)。
环境准备:Maven依赖与基本引入
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.2.0-jre</version> <!-- 请使用最新稳定版 -->
</dependency>
import com.google.common.util.concurrent.RateLimiter;
五种常用API详解与代码示例
创建:RateLimiter.create(double permitsPerSecond)
创建每秒允许 permitsPerSecond 次请求的限流器。
RateLimiter limiter = RateLimiter.create(5.0); // 每秒5个请求
阻塞获取:acquire() / acquire(int permits)
如果获取不到令牌,线程会阻塞等待直到令牌可用。
limiter.acquire(); // 申请1个令牌,阻塞直到成功 limiter.acquire(3); // 申请3个令牌,可能阻塞更久
非阻塞尝试:tryAcquire() / tryAcquire(long timeout, TimeUnit unit)
如果无法立即获取令牌,直接返回false(或等待指定超时时间)。
if (limiter.tryAcquire()) {
System.out.println("获取令牌成功,执行业务");
} else {
System.out.println("限流!");
}
// 等待500毫秒
if (limiter.tryAcquire(500, TimeUnit.MILLISECONDS)) {
// ...
}
设置速率:setRate(double permitsPerSecond)
动态调整限流速率,无需重新创建实例。
limiter.setRate(10.0); // 改为每秒10个请求
获取当前速率:getRate()
double currentRate = limiter.getRate(); // 返回当前速率
完整示例:模拟每秒5次请求
RateLimiter limiter = RateLimiter.create(5.0);
for (int i = 0; i < 10; i++) {
double waitTime = limiter.acquire();
System.out.println("请求" + i + "等待时间: " + waitTime + "秒");
}
// 输出:前5个几乎无等待,后5个每个等待约0.2秒
实际场景:秒杀系统、API限流、数据库连接池
场景1:秒杀接口限流
防止用户疯狂点击导致后端崩溃。
@PostMapping("/seckill")
public String seckill(@RequestParam Long productId) {
if (!rateLimiter.tryAcquire()) {
return "服务器繁忙,请稍后再试";
}
// 业务逻辑:扣库存、生成订单
return "抢购成功";
}
场景2:数据库连接池保护
控制每秒的数据库查询次数,防止慢查询堆积。
RateLimiter dbLimiter = RateLimiter.create(100); // 每秒最多100次查询
public Result queryData(String sql) {
dbLimiter.acquire(); // 阻塞等待
return jdbcTemplate.query(sql);
}
场景3:API网关全局限流
对第三方API调用进行速率控制(如微信支付接口限制)。
常见问题FAQ:如何调参?如何处理拒绝?
Q:如何调整RateLimiter的最大等待时间?
A:使用带超时的 tryAcquire(),而非直接设置最大等待时长,RateLimiter本身不支持“最多等待X秒”,但可通过业务代码配合 CompletableFuture 实现。
Q:请求被拒绝后,客户端如何处理?
- HTTP接口:返回503状态码,并包含
Retry-After响应头。 - 内部调用:使用重试队列或降级逻辑(如使用缓存数据)。
Q:RateLimiter可以用于分布式限流吗?
A:不能,RateLimiter是单机版,分布式场景需使用Redis + Lua脚本或Sentinel等框架。
性能对比:RateLimiter vs Semaphore vs 分布式方案
| 维度 | RateLimiter | Semaphore | Redis + Lua |
|---|---|---|---|
| 控制维度 | 速率(QPS) | 并发数 | 速率/并发 |
| 是否阻塞 | 可选阻塞/非阻塞 | 阻塞 | 非阻塞 |
| 适用场景 | 单机限流 | 线程池管理 | 分布式集群 |
| 性能 | 极低延迟(微秒级) | 同左 | 毫秒级(网络开销) |
| 平滑能力 | 支持(预热/突发) | 无 | 需自实现 |
一句话总结:单机限流首选RateLimiter;并发控制用Semaphore;分布式限流用Redis。
何时用RateLimiter?最佳实践
- 适用场景:需要控制某个操作的总调用速率(如:每秒不超过10次外部API调用)。
- 不适用场景:需要精确控制并发线程数(此时用线程池)、需要跨服务限流(用分布式方案)。
- 最佳实践:
- 使用
tryAcquire()避免阻塞关键业务。 - 结合熔断(如Hystrix)形成完整保护链。
- 预热模式(
SmoothWarmingUp)适用于启动阶段敏感的服务。
- 使用
最后提醒:RateLimiter的 acquire() 是同步阻塞的,在高IO场景下请勿在事件循环中调用,建议使用 tryAcquire() 异步处理。
(文章基于Guava 33.x版本编写,所有代码均可直接运行。)