bigywater.tech
Spring · 124

Gateway 限流:RequestRateLimiter 与 Redis

Gateway 限流到底限了什么?key 从哪里来?为什么没配 KeyResolver 全部 403?多个网关实例会不会各限各的?Redis 挂了限流器是放行还是挡住?这篇在 Spring Cloud Gateway 5.0.3 上接一个自己起的 Redis,一个个量出来。

5 个 200
burstCapacity=5 时连续 10 个请求放行的个数,其余 429,后端只收到 5 个
403
没有 KeyResolver、拿不到 key 时的状态码
卡住
Redis 停掉后,默认配置下请求 12 秒都没有响应;配 timeout=1s 才放行

令牌桶

实测 · 本机执行输出
  状态码序列 [200, 200, 200, 200, 200, 429, 429, 429, 429, 429];后端收到 5 个请求
  第 1 个响应的限流头 {'remaining': '4', 'requested-tokens': '1', 'burst-capacity': '5', 'replenish-rate': '2'};第 5 个 {'remaining': '0', 'requested-tokens': '1', 'burst-capacity': '5', 'replenish-rate': '2'};第 6 个(被拒绝){'remaining': '0', 'requested-tokens': '1', 'burst-capacity': '5', 'replenish-rate': '2'}
  Redis 里的 key:['request_rate_limiter.{r1.alice}.timestamp', 'request_rate_limiter.{r1.alice}.tokens'];tokens 的 TTL = 5 秒
  等 1.1 秒后再发 4 个:[200, 200, 429, 429]
  另一个用户 bob 紧接着发 6 个:[200, 200, 200, 200, 200, 429]

容量与补充速率;响应头带剩余令牌;Redis 键带路由 id。

KeyResolver

实测 · 本机执行输出
  没有配置 KeyResolver bean(用默认的 PrincipalNameKeyResolver,请求没有登录用户)→ HTTP 403
  加 deny-empty-key=false → HTTP 200
  KeyResolver 取 X-User,请求不带 X-User → HTTP 403;加 status-code=SERVICE_UNAVAILABLE 后被限流的状态码序列 [200, 200, 200, 200, 200, 503, 503]

拿不到 key 返回 403,可以关闭;状态码可改。

requestedTokens

实测 · 本机执行输出
  连续 6 个请求:[200, 200, 429, 429, 429, 429]
  限流头 {'remaining': '1', 'requested-tokens': '2', 'burst-capacity': '5', 'replenish-rate': '2'}

每个请求消耗多个令牌。

多实例

实测 · 本机执行输出
  交替发给两个实例的 10 个请求:[200, 200, 200, 200, 200, 429, 429, 429, 429, 429];后端收到 5 个

计数在 Redis,多个实例共享。

Redis 故障

实测 · 本机执行输出
  [默认的 Redis 命令超时] Redis 正常时第一个请求 200(0.2s);Redis 停掉之后连续 3 个请求 ['超时(>12s)', '超时(>12s)', '超时(>12s)'];后端收到 0 个
  [spring.data.redis.timeout=1s] Redis 正常时第一个请求 200(0.2s);Redis 停掉之后连续 3 个请求 ['200(1.1s)', '200(1.1s)', '200(1.1s)'];后端收到 3 个

默认卡住请求,配 Redis 超时后放行。

分步演示

限流
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。Redis 是自己起的独立实例(端口 7395);key 取请求头 X-User;请求是顺序发出的,最后补出的令牌数受请求间隔影响,所以个别次的放行数可能多 1 个;后端是立即返回的 Python HTTP 服务
    • 没有验证:用 Redis Lua 脚本的细节与时钟问题;Redis Cluster / Sentinel 下的行为;自定义 RateLimiter 实现;限流粒度换成 IP 的取值(X-Forwarded-For 的信任问题见第 121 篇);Gateway Server MVC 的限流;默认 Redis 命令超时的具体数值(只测到 12 秒内没有响应);fail-open 是否是限流器的设计意图(只观察到现象)
    京ICP备2026067502号-1京公网安备11010502063487号