Guava RateLimiter怎么用?

wen python案例 3

Guava RateLimiter怎么用?从入门到实战的完整指南

目录导读

  • 什么是Guava RateLimiter?为什么需要它?
  • RateLimiter的核心原理:令牌桶算法与平滑突发
  • 环境准备:Maven依赖与基本引入
  • 五种常用API详解与代码示例
  • 实际场景:秒杀系统、API限流、数据库连接池
  • 常见问题FAQ:如何调参?如何处理拒绝?
  • 性能对比:RateLimiter vs Semaphore vs 分布式方案

什么是Guava RateLimiter?为什么需要它?

Q:什么是RateLimiter?
A:RateLimiter是Google Guava库提供的一个限流工具,基于令牌桶算法实现,它控制单位时间内允许通过的请求数量,常用于防止系统被突发流量冲垮。

Guava RateLimiter怎么用?

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版本编写,所有代码均可直接运行。)

抱歉,评论功能暂时关闭!