Spring Cloud Gateway 限流别把所有请求都当成一个用户网关限流看起来简单给接口设置 QPS超过就拒绝。但生产环境里限流维度如果设计不好很容易误伤。所有用户共用一个限流桶大客户被小客户影响只按 IP 限流NAT 后一群用户被当成一个人只按用户限流又挡不住某个接口被打爆。Spring Cloud Gateway 限流要按业务维度设计而不是只配一个全局阈值。一、限流维度要分层flowchart TD A[Request] -- B[Global Limit] B -- C[Tenant Limit] C -- D[User Limit] D -- E[Route Limit] E -- F[Backend Service]全局限流保护平台租户限流保证公平用户限流防滥用路由限流保护具体后端。不同层的目标不一样。分层限流还要注意层级间的相互作用。如果全局限流设置得太低会抵消掉租户级的配额如果租户限流太松单个大客户可能占满全局配额。一种做法是让请求依次通过各层限流任何一层拒绝就返回另一种做法是只在最细粒度限流上层只做监控和告警。前者保护更严格后者更灵活。选择哪种取决于你的业务模式和服务承诺。二、KeyResolver 要贴近业务身份Gateway 的限流通常需要KeyResolver。不要默认只用 IP最好根据租户、用户、接口组合生成 key。Bean public KeyResolver tenantUserKeyResolver() { return exchange - { String tenant exchange.getRequest().getHeaders().getFirst(X-Tenant-Id); String user exchange.getRequest().getHeaders().getFirst(X-User-Id); String path exchange.getRequest().getPath().value(); return Mono.just(tenant : user : path); }; }真实系统里租户和用户身份应该来自认证结果不要信任外部随便传的 header。如果请求来自微服务内部调用可能没有用户身份。这时可以考虑用服务名、实例 ID 或调用链 traceId 作为限流 key。内部调用通常信任度更高限流可以更宽松但也要防止某个服务 bug 导致疯狂调用下游。内部限流的目标不是限制正常流量而是快速发现异常。三、不同接口要有不同策略登录、查询、导出、AI 生成、支付回调限流策略不可能一样。高成本接口要更严格可缓存接口可以更宽松核心链路要配合降级。rate_limit_policy: /api/search: replenishRate: 50 burstCapacity: 100 /api/ai/generate: replenishRate: 5 burstCapacity: 10 /api/export: replenishRate: 1 burstCapacity: 2限流配置要能按环境和租户调整。企业客户、免费用户、内部系统调用通常不该混用一套阈值。四、限流响应要可观测限流不是简单返回 429。要记录 route、key、租户、剩余额度和拒绝原因方便排查误伤。{ event: rate_limited, route: /api/ai/generate, tenant: t_01, key_type: tenant_user_route, limit: 5, retry_after_ms: 12000 }客户端也需要明确的Retry-After否则用户只会看到失败不知道什么时候重试。限流时的用户体验也很重要。对于 Web 页面可以展示当前访问人数较多预计等待 X 秒对于 API 调用可以返回明确的错误码和建议重试时间对于移动端可以在客户端实现指数退避重试避免用户手动反复刷新。好的限流系统不只是拒绝请求而是在保护系统的同时尽量减少对用户的影响。在多网关实例的部署场景中基于内存的限流方案如 Resilience4j RateLimiter存在一个根本问题每个实例独立计数总限流额度 单实例额度 × 实例数。假设配置全局 QPS 为 10004 个实例意味着实际 QPS 上限是 4000失去了限流的意义。解决方案是使用 Redis 作为分布式计数器通过 Lua 脚本保证令牌获取的原子性。但 Redis 方案也有代价——每个请求增加一次 Redis 调用约 1-2ms 延迟在高 QPS 场景下需要对 Redis 做分片或使用本地预取令牌优化。我们的折中方案是本地内存维护一个小的令牌缓冲Redis 每次下发 100 个令牌到本地实例在本地消耗令牌耗尽时异步向 Redis 申请下一批。这样 Redis 调用频率从每请求一次降为每 100 个请求一次延迟开销可以忽略不计同时误差控制在 10% 以内。五、总结Spring Cloud Gateway 限流要分层设计全局、租户、用户、路由分别保护不同目标。KeyResolver 要基于可信业务身份不同接口配置不同阈值限流事件必须可观测。限流不是为了拒绝用户而是为了让系统在压力下保持秩序。维度设计对了才不会把所有请求都当成一个用户。
Spring Cloud Gateway 限流:别把所有请求都当成一个用户
发布时间:2026/7/3 20:17:56
Spring Cloud Gateway 限流别把所有请求都当成一个用户网关限流看起来简单给接口设置 QPS超过就拒绝。但生产环境里限流维度如果设计不好很容易误伤。所有用户共用一个限流桶大客户被小客户影响只按 IP 限流NAT 后一群用户被当成一个人只按用户限流又挡不住某个接口被打爆。Spring Cloud Gateway 限流要按业务维度设计而不是只配一个全局阈值。一、限流维度要分层flowchart TD A[Request] -- B[Global Limit] B -- C[Tenant Limit] C -- D[User Limit] D -- E[Route Limit] E -- F[Backend Service]全局限流保护平台租户限流保证公平用户限流防滥用路由限流保护具体后端。不同层的目标不一样。分层限流还要注意层级间的相互作用。如果全局限流设置得太低会抵消掉租户级的配额如果租户限流太松单个大客户可能占满全局配额。一种做法是让请求依次通过各层限流任何一层拒绝就返回另一种做法是只在最细粒度限流上层只做监控和告警。前者保护更严格后者更灵活。选择哪种取决于你的业务模式和服务承诺。二、KeyResolver 要贴近业务身份Gateway 的限流通常需要KeyResolver。不要默认只用 IP最好根据租户、用户、接口组合生成 key。Bean public KeyResolver tenantUserKeyResolver() { return exchange - { String tenant exchange.getRequest().getHeaders().getFirst(X-Tenant-Id); String user exchange.getRequest().getHeaders().getFirst(X-User-Id); String path exchange.getRequest().getPath().value(); return Mono.just(tenant : user : path); }; }真实系统里租户和用户身份应该来自认证结果不要信任外部随便传的 header。如果请求来自微服务内部调用可能没有用户身份。这时可以考虑用服务名、实例 ID 或调用链 traceId 作为限流 key。内部调用通常信任度更高限流可以更宽松但也要防止某个服务 bug 导致疯狂调用下游。内部限流的目标不是限制正常流量而是快速发现异常。三、不同接口要有不同策略登录、查询、导出、AI 生成、支付回调限流策略不可能一样。高成本接口要更严格可缓存接口可以更宽松核心链路要配合降级。rate_limit_policy: /api/search: replenishRate: 50 burstCapacity: 100 /api/ai/generate: replenishRate: 5 burstCapacity: 10 /api/export: replenishRate: 1 burstCapacity: 2限流配置要能按环境和租户调整。企业客户、免费用户、内部系统调用通常不该混用一套阈值。四、限流响应要可观测限流不是简单返回 429。要记录 route、key、租户、剩余额度和拒绝原因方便排查误伤。{ event: rate_limited, route: /api/ai/generate, tenant: t_01, key_type: tenant_user_route, limit: 5, retry_after_ms: 12000 }客户端也需要明确的Retry-After否则用户只会看到失败不知道什么时候重试。限流时的用户体验也很重要。对于 Web 页面可以展示当前访问人数较多预计等待 X 秒对于 API 调用可以返回明确的错误码和建议重试时间对于移动端可以在客户端实现指数退避重试避免用户手动反复刷新。好的限流系统不只是拒绝请求而是在保护系统的同时尽量减少对用户的影响。在多网关实例的部署场景中基于内存的限流方案如 Resilience4j RateLimiter存在一个根本问题每个实例独立计数总限流额度 单实例额度 × 实例数。假设配置全局 QPS 为 10004 个实例意味着实际 QPS 上限是 4000失去了限流的意义。解决方案是使用 Redis 作为分布式计数器通过 Lua 脚本保证令牌获取的原子性。但 Redis 方案也有代价——每个请求增加一次 Redis 调用约 1-2ms 延迟在高 QPS 场景下需要对 Redis 做分片或使用本地预取令牌优化。我们的折中方案是本地内存维护一个小的令牌缓冲Redis 每次下发 100 个令牌到本地实例在本地消耗令牌耗尽时异步向 Redis 申请下一批。这样 Redis 调用频率从每请求一次降为每 100 个请求一次延迟开销可以忽略不计同时误差控制在 10% 以内。五、总结Spring Cloud Gateway 限流要分层设计全局、租户、用户、路由分别保护不同目标。KeyResolver 要基于可信业务身份不同接口配置不同阈值限流事件必须可观测。限流不是为了拒绝用户而是为了让系统在压力下保持秩序。维度设计对了才不会把所有请求都当成一个用户。