1526 字
约 5 分钟
0
Guava 限流入门
Guava 限流入门
1. 一句话简介
Guava 是 Google 开源的 Java 工具库,其中 RateLimiter(令牌桶默认实现 SmoothBurty)用于对代码段的执行速率做平滑限制,即「限流」。核心思路是:以固定速率向令牌桶中放令牌,请求执行前必须先拿到一个令牌,令牌耗尽时请求被拒绝,从而把瞬时流量摊平、保护下游服务不被冲垮。
在 Spring Boot 中(参照本 demo),通常把它与 AOP 结合做成「声明式限流」:自定义一个 @RateLimiter 注解标记在接口方法上,再由切面 RateLimiterAspect 拦截被标注的方法,调用 RateLimiter.tryAcquire(timeout, timeUnit) 尝试取令牌——拿到则放行执行 point.proceed(),拿不到则抛出异常交给全局异常处理器返回友好的提示。业务代码只加一个注解即可获得限流能力,与限流逻辑完全解耦。
2. 什么时候使用
- ✅ 单机部署、单实例限流:Guava
RateLimiter状态只存在当前 JVM 进程内,天然适合单体或单实例应用,无需任何外部依赖。 - ✅ 对接口做 QPS 上限保护:需要精确控制「每秒最多允许 N 次请求」,如本 demo 中
@RateLimiter(value = 1.0)表示每秒最多放行 1 次。 - ✅ 想用最少代码接入、快速落地:只需引一个
guava依赖 + 一个切面类,配上注解即可,无需引入 Redis、MQ 等外部组件。 - ✅ 需要平滑限流而非直接拦截:令牌桶算法允许一定程度的突发、频率放行平滑,比冷硬的「滑动窗口一刀切拒绝」体验更好。
- ✅ 只对部分接口限流:基于
@annotation切入点精准拦截,没加注解的接口完全不受影响(对应 demo 中test2对照接口),侵入面小。 - ❌ 分布式/多实例集群限流:
RateLimiter状态不能跨进程共享,每个实例各自计数,集群下无法做到全局阈值一致。需改用 Redis + Lua 或 Sentinel 等分布式方案。 - ❌ 接口级精确的流量整形与复杂场景:
RateLimiter只控制速率,不做请求去重、内存级熔断降级、动态规则下发等高级治理。 - ❌ 需要限流规则动态可配置/可热更新:Guava 限流器参数需在代码里写死或用
setRate()手动调整,缺乏管理端支持。 - ❌ 单点进程内状态,重启即清零:进程重启后限流器重新创建,无法持久化历史流量与配额。
3. 常见业务场景
- 接口防刷、防恶意频繁访问:对公开 API(如查询/登录接口)加上限流,防止被脚本高频轮询打爆。贴合本 demo 的
test1(QPS=1)演示——快速刷新时返回「手速太快了,慢点儿吧〜」而不是去执行业务。 - 保护下游关键资源(DB / 第三方接口 / 令牌):当业务逻辑会调用外部受配额限制的服务(短信、支付、OpenAI 等)时,先用
RateLimiter收敛本地调用速率,避免超额。demo 中通过RATE_LIMITER_CACHE(ConcurrentHashMap)按方法缓存限流器,保证每个接口独立、稳定地限速。 - 突发流量平滑 / 削峰:秒杀、抢单等高并发瞬间涌入时,用令牌桶把尖峰流量摊平到一段时间内处理,避免服务瞬时过载。
tryAcquire(timeout, timeUnit)支持短暂排队等待,比硬拒绝更柔和。 - 功能开关式的按接口差异化限流:通过
@RateLimiter的value/qps参数,不同接口配置不同阈值(demo 中test3的 QPS=2 就是test1的两倍),实现同一套切面下的差异化治理。 - 为手动/定时任务做自我保护:对某些需要读库、拉外部数据但又不要求实时的后台任务,用限流控制执行节奏,避免一次性拉取过大。
4. 同类技术对比
| 维度 | Guava RateLimiter(本 demo) | Redis + Lua(滑动窗口/令牌桶) | Alibaba Sentinel | JDK Semaphore |
|---|---|---|---|---|
| 算法/能力 | 令牌桶,控制速率 | 可自实现固定窗口/滑动窗口,控制速率与窗口计数 | 限流+熔断+降级(并发数、QPS、链路) | 信号量,只控制并发数 |
| 分布式能力 | 仅单机 | 强,跨实例全局统一 | 支持集群,配合控制台 | 仅单机 |
| 外部依赖 | 无(纯 JVM 库) | 需 Redis | 需引入客户端/控制台(较重) | 无(JDK 内置) |
| 易用性/接入成本 | 极低,注解+AOP 即插即用 | 中,需写 Lua 脚本并维护分布式一致性 | 较高,依赖重、学习曲线陡 | 低,写少量同步代码 |
| 动态规则/治理 | 弱,参数写死,进程内状态 | 可通过脚本/配置下发 | 强,控制台可视化配置与实时监控 | 弱 |
| 适用规模 | 单实例、中小场景、快速落地 | 分布式/多实例、需要全局阈值 | 大规模微服务、复杂治理需求 | 单纯控制并发上限 |
选型建议:单机应用、想用最少成本快速给接口加 QPS 上限,选 Guava RateLimiter 足够(也是本模块演示的方案);一旦应用横向扩展成多实例集群、需要全局统一的限流阈值,应切换到 Redis + Lua 或改为自实现分布式令牌桶;若面对的是大型微服务、还要熔断降级、动态规则和可视化治理,直接上 Sentinel;而如果目标只是「限制同时进行的并发数」(而非速率),用 JDK 自带 Semaphore 即可,无需任何三方依赖。
评论
0 条
还没有评论,先写一条吧。