ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Redisson 分布式限流实战:3 步给 API 网关装上流量闸门

Redisson 分布式限流实战:3 步给 API 网关装上流量闸门 Redisson 分布式限流实战3 步给 API 网关装上流量闸门【免费下载链接】redissonRedisson: 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..项目地址: https://gitcode.com/GitHub_Trending/re/redisson大促零点单机限流器明明拦住了 3000 QPS后端还是被打穿——流量摊到 8 台网关每台只看到自己那一份。把配额挪进 Redis用 Redisson 的分布式限流让全集群共用一个桶本文给出从建实例到网关过滤器的完整落地路径。单机限流器为什么在集群下失效3 个盲区定位先排除一个常见误区限流没生效往往不是限流器坏了而是限流范围错了。盲区一配额被实例数放大。Guava 的 RateLimiter、JUC 的信号量都是进程内对象。8 台网关每台放 100 QPS集群实际放行 800 QPS是配置的 8 倍。后端看到的是 800你以为配的是 100对不上账。盲区二配额随扩缩容漂移。网关按 HPA 自动扩到 16 台单机配额不变集群上限直接翻倍。想临时压回去只能改每台实例的配置——8 次发布或者干脆放弃。盲区三节点故障导致配额漏气。一台网关挂了它的配额跟着消失集群总配额被动收缩。反过来扩容瞬间新节点还没预热配额又短暂偏高。运维侧看不到一个稳定的集群级配额数字。根因只有一个配额状态散落在各进程内存里没有共享存储。解法也只有一个——把桶挪到所有节点都能访问的地方Redis 是最顺手的候选。Redisson 限流器原理令牌桶 Lua 脚本一次原子扣减令牌桶一个桶按固定速率注水比如每秒 100 枚桶容量 速率。请求来就拿一枚桶空了要么等、要么拒。比喻成水龙头匀速滴水进杯子杯子满了新水溢出——突发流量能短时吸收桶里的存量持续超载会被限住注水速率封顶。为什么状态放 Redis所有网关节点从同一个 key 取令牌配额天然共享扩容缩容不影响总量。为什么必须用 Lua 脚本一次取令牌包含 4 步——读当前剩余、判断够不够、扣减、把已用令牌连同时间戳记进 ZSet。如果拆成 4 条独立 Redis 命令两个节点并发时都会读到剩 1 枚都判定通过都扣成 0最终超发。Redisson 把这 4 步打包成一段 Lua 脚本通过EVAL一次性发给 Redis。Redis 单线程执行脚本脚本内不可能被别的命令插队读-判-扣-记就是一个原子事务。这就是分布式环境下的扣减一致性来源。令牌怎么补回来已消耗令牌带时间戳存在 ZSet 里score 是消耗时刻。每次取令牌前脚本先用zrangebyscore捞出超过 rateInterval 没被释放的旧令牌累加回当前值再从 ZSet 里删掉。不需要后台线程定时补补桶逻辑嵌在每次取令牌的路径里。这套脚本的 Java 封装就是RedissonRateLimiterredisson/src/main/java/org/redisson/RedissonRateLimiter.java对使用者只暴露几个方法。3 步配置 RRateLimiter建实例、设速率、取许可第 1 步建实例。限流器名字就是 Redis key 前缀命名带业务语义方便排查。Config config new Config(); config.useSingleServer().setAddress(redis://127.0.0.1:6379); RedissonClient client Redisson.create(config); RRateLimiter limiter client.getRateLimiter(gw:order:create);第 2 步设速率。trySetRate只在限流器还没初始化时生效已存在就跳过不会覆盖线上配置。limiter.trySetRate(RateType.OVERALL, 100, 1, RateIntervalUnit.SECONDS); // rate100窗口内放行上限 // rateInterval1 SECONDS窗口长度 1 秒第 3 步取许可。tryAcquire()立即返回不等待。if (limiter.tryAcquire()) { // 放行执行真实业务 } else { // 拒绝返回 429 或直接丢弃 }三个参数各管一件事rate定窗口内总量rateInterval unit定窗口时长RateType定配额归属范围。跑通这三步限流器就活了。网关过滤器怎么接Spring Cloud Gateway 放行与 429 分支Spring Cloud Gateway 是响应式的过滤器返回MonoVoid。限流判定塞在filter方法里两条分支拿到许可 → 走chain.filter(exchange)继续后续过滤器链没拿到 → 直接置 429 并 complete不往后走。public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { String path exchange.getRequest().getPath().value(); RRateLimiter limiter client.getRateLimiter(gw: path); limiter.trySetRate(RateType.OVERALL, 100, 1, RateIntervalUnit.SECONDS); if (limiter.tryAcquire()) { return chain.filter(exchange); } exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); return exchange.getResponse().setComplete(); }key 怎么拼全局配额用路径要按用户限就把 userId 拼进来gw: userId : path。一个业务维度一个 key互不干扰。trySetRate每请求都调没关系它底层是HSETNX已初始化时直接 no-op不会把线上速率改掉。所以过滤器里每请求先 trySetRate 再 tryAcquire是安全写法首次部署自动完成初始化后续请求零副作用。一个要留意的点tryAcquire()是同步阻塞调用。在 WebFlux 的 event loop 里调它会占住事件循环线程。生产环境建议改用tryAcquireAsync()拿到RFuture再包一层Mono.fromFuture把等待交还 reactor 调度别阻塞 event loop。RateLimiter 配置选型OVERALL 与 PER_CLIENT 对比 调速避坑两种 RateType 怎么选维度OVERALLPER_CLIENT配额归属所有客户端共享一个桶每个 Redisson 客户端实例独立一个桶典型场景保护下游接口集群总吞吐封顶防单租户刷爆允许整体流量高扩容影响配额固定与节点数无关客户端越多总吞吐线性放大Redis 键单一value键带 clientId 后缀的独立value键适合做网关全局 QPS 闸用户级 / 租户级 API 配额判断标准一句话你要限制的是总盘子还是每个玩家的分额。总盘子选 OVERALL分额选 PER_CLIENT。两者也可以叠加——外层 OVERALL 保下游内层 PER_CLIENT 防单点滥用。三个进阶提示动态调速setRate(...)会强制覆盖配置同时DEL掉当前令牌状态value 和 permits 键都清。等价于发一个新满桶。高流量时调用旧请求已经扣掉的额度瞬间归零可能引发一次放行尖峰。只调速率、不想清桶目前 API 层面没有保留状态改速率的路径要么接受清桶副作用要么用trySetRate走未初始化才生效的语义。批量取许可acquire(10)一次扣 10 枚。适合一个上游请求背后扇出 10 次下游调用的场景按真实消耗配额比按请求数限更贴近后端压力。异步取许可tryAcquireAsync()返回RFutureBoolean不阻塞当前线程。WebFlux 环境用它套Mono.fromFuture后.subscribe处理两条分支。容易踩的坑tryAcquire(permits)的 permits 不能大于 rate。Lua 脚本里有assert(tonumber(rate) tonumber(ARGV[1]))超了直接抛错。setRate 会清桶线上动态调速前想清楚清空瞬间的放行尖峰能否接受。限流器 key 建议设过期。trySetRate支持keepAliveTime参数或事后对 key 调expire。不设过期的话Redis 里会积累一堆历史业务的空桶长期占用内存。PER_CLIENT 的客户端是 Redisson 实例 ID不是 HTTP 用户 ID。同一业务里所有网关节点共享同一个 Redisson 实例 ID 的话它们会被视为同一个客户端。要按业务用户维度分桶得在 key 名里手动拼 userId。收尾分布式限流的核心不是算法而是配额状态放哪里、谁保证扣减原子。Redisson 把这两件事都压在 Redis Lua 上业务侧只剩三行代码。落地时先确认 OVERALL 还是 PER_CLIENT再决定 key 粒度和速率窗口剩下的交给RRateLimiter。官方文档docs/overview.md【免费下载链接】redissonRedisson: 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..项目地址: https://gitcode.com/GitHub_Trending/re/redisson创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表