如何用 Redisson 分布式限流给高并发 API 网关装上流量闸门

如何用 Redisson 分布式限流给高并发 API 网关装上流量闸门

【免费下载链接】redisson Redisson: Valkey & Redis Java Client and Real-Time Data Platform. Sync/Async/RxJava/Reactive API. Over 50 Valkey and Redis based Java objects and services: Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Spring, Tomcat, Scheduler, JCache API, Hibernate, RPC, local cache.. 【免费下载链接】redisson 项目地址: https://gitcode.com/GitHub_Trending/re/redisson

晚上 20:00,秒杀活动准时开始。第 1 秒,网关涌进 3 万个请求;第 5 秒,数据库连接池被打穿;第 9 秒,整个链路雪崩。这不是段子,而是许多团队真实经历过的"大促惨案"。

要守住 API 网关这条防线,Redisson 分布式限流是最趁手的工具之一。Redisson 是 Valkey 与 Redis 的 Java 客户端,内置了 50 多种分布式对象与服务,限流正是其中之一——它把判断"放不放行"这件事搬到了 Redis 端,用一段原子执行的 Lua 脚本,让所有网关实例共享同一把流量闸门。本篇不讲空理论,只讲"为什么崩、限什么、怎么用",帮你把第一道防线真正搭起来。

流量一来,服务为什么先崩?

后端资源永远是有限的:数据库连接数有限、线程池有限、下游第三方接口也有配额。当请求像洪水一样涌来时,最先被冲垮的往往不是网关本身,而是它身后的服务——连接池耗尽、队列积压、超时重试又带来更多请求,形成雪崩循环。

限流的本质,就是提前承认"我们接不住所有流量",然后主动把超出的部分挡在外面。它就像高速路收费站的发卡杆:车道就那么多,单位时间放行多少辆车是定死的,多出来的车在闸口排队或改道,而不是一起涌上主路把收费站挤爆。

限流不是"数数",背后是算法之争

很多人以为限流就是"每秒记一次数,超了就拦",真正动手才发现里面有讲究。常见的做法大致有四种:

方案核心思路优点短板
固定窗口按秒/分钟切块,每块内计数实现简单窗口边界会出现"双倍流量"突刺
滑动窗口统计"最近一个周期"内的请求边界更平滑需要保存时间戳,成本略高
令牌桶匀速补充令牌,请求消耗令牌允许突发流量桶容量需要调参
漏桶请求按固定速率流出输出绝对平滑无法应对突发,可能丢弃能力内流量

这里没有"最好的算法",只有"最合适的场景"。但如果每一种都从零手写,你得处理存储、过期、并发,很快就会发现限流这件小事被写成了一个大项目。

单机限流很简单,分布式限流才是真麻烦

网关从来不是一台机器。你部署了 10 个网关实例,每个实例各自维护一份计数器——单看每一台都"没超限",合起来却把后端打爆了。这就是高并发场景限流最常见的坑:限流状态必须全局共享

更要命的是并发竞态。传统写法是"先查计数,再判断,再扣减",三步之间只要有另一个请求插进来,计数就会出错;再加上多台机器时钟不一致,滑动窗口的"现在"到底以谁为准?在分布式环境下,靠 JVM 内存和本地时间做限流,几乎必然翻车。

所以正确姿势是找一个所有实例都信任的裁判——Redis 天然适合这个角色:它是共享存储,它单线程执行命令天然串行,而 Lua 脚本能保证多条操作一次性原子完成。

Redisson 把限流做成了"开箱即用"的组件

这就是 Redisson 的价值:它没有让你去写 Redis 命令,而是把整个限流器封装成了 RRateLimiter 接口,由 RedissonRateLimiter 类实现(源码在 redisson/src/main/java/org/redisson/RedissonRateLimiter.java,接口在 redisson/src/main/java/org/redisson/api/RRateLimiter.java)。你拿到的不是一段示例代码,而是一个生产可用的组件。

它的底层设计也相当精巧:

  • 一个 Hash 保存限流配置:rate(速率)、interval(时间间隔)、type(限流类型);
  • 一个 value 键 记录当前剩余许可数;
  • 一个 有序集合(zset) 记录每次许可发放的时间戳与数量,时间窗口滑过后自动"回收"过期许可——相当于把滑动窗口算法原样落到了 Redis 里;
  • 整个"检查-扣减-记录"过程用 Lua 脚本原子执行,多台网关并发获取许可也不会互相踩踏。

另外,RateType 提供了两种粒度:OVERALL 是全局总量限流,所有实例共享一个额度;PER_CLIENT 则是按客户端维度限流,每个调用方各自独立计数,适合做"每用户每秒最多 N 次"之类的接口配额。多数网关场景用 OVERALL,做账号体系限流时再用 PER_CLIENT,两者配合覆盖了绝大多数需求。

三步把 Redisson 限流接进 API 网关

接入过程比想象中短,核心就三步:拿到限流器、设置速率、尝试获取许可。下面这段代码几乎是全部骨架:

RRateLimiter limiter = redissonClient.getRateLimiter("rate_limit:" + path);
limiter.trySetRate(RateType.OVERALL, 100, Duration.ofSeconds(1));
boolean acquired = limiter.tryAcquire();

trySetRate 只在限流器首次创建时生效,重复调用不会覆盖已有配置,天然规避了多实例重复初始化的尴尬;tryAcquire() 就是"尝试获取限流许可",成功返回 true 放行,失败返回 false,网关直接回一个 429 Too Many Requests 即可,后端连接一个多余请求都接不到。

这里还有一个容易纠结的选择:tryAcquireacquire 到底用哪个?记住一条准则就行:

方法行为适合场景
tryAcquire()拿不到许可立即返回 false,不等待网关过滤、快速拒绝,本篇主推
tryAcquire(permits, timeout, unit)在超时时间内等待许可业务内"排队等一会"的柔性场景
acquire()一直阻塞到拿到许可为止慎用,容易把等待线程堆成新瓶颈

进阶:限流速率动态调整的正确姿势

限流不是配一次就一劳永逸。大促开始时你可能想把阈值调高,活动结束后再降下来。这时候用 updateRate 配合 RateLimiterArgs,可以动态修改速率而无需重建限流器:

limiter.updateRate(RateLimiterArgs.of(RateType.OVERALL, 500, Duration.ofSeconds(1)).keepState(true));

注意那个 .keepState(true)——它表示调整速率时保留当前已积累的许可状态,避免每次调参都把计数清零、给下游造成一次瞬时脉冲。如果限流器长期闲置,还可以用 trySetRatekeepAliveTime 参数让它自动过期清理,省得 Redis 里堆一堆没人用的键。

顺带一提,acquire(10) 这类批量获取许可的用法,适合"一次操作消耗多个配额"的场景,比如批量任务提交,按业务权重计费,而不是每个请求都固定消耗 1。

动手试试,把第一道防线搭起来

限流的价值不在于代码多华丽,而在于它让"最坏情况"变得可控:流量再大,后端永远只看到它能承受的那部分。把 getRateLimiter 加进你的网关过滤器、给不同接口配上不同的速率、再盯一盯被拒绝请求的曲线,第一道防线就算立住了。更完整的 API 说明可以翻阅项目文档 docs/overview.md,遇到具体接口的细节,直接去 RRateLimiter 的源码注释里找答案,往往比任何教程都准确。🚀

【免费下载链接】redisson Redisson: Valkey & Redis Java Client and Real-Time Data Platform. Sync/Async/RxJava/Reactive API. Over 50 Valkey and Redis based Java objects and services: Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Spring, Tomcat, Scheduler, JCache API, Hibernate, RPC, local cache.. 【免费下载链接】redisson 项目地址: https://gitcode.com/GitHub_Trending/re/redisson

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值