找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4294

积分

0

好友

564

主题
发表于 18 小时前 | 查看: 4| 回复: 0

《十万个why》系列持续更新中。

用 Spring Cloud Gateway 的团队基本都知道它自带限流功能,配合 Redis 就能实现令牌桶限流。但你去看任何一家稍有规模的公司的架构图,流量进来之后第一道限流几乎都在 Nginx 层,Gateway 的限流反而是第二道甚至第三道。

既然 Gateway 已经能限流了,为什么还要在 Nginx 层再做一遍?

这两层限流解决的问题完全不同,缺一不可。

Gateway 的限流是怎么工作的

Spring Cloud Gateway 内置了 RequestRateLimiterGatewayFilterFactory,底层默认用 Redis + Lua 脚本实现令牌桶算法。配置起来很简单:

spring:
  cloud:
     gateway:
       routes:
         - id: order-service
           uri: lb://order-service
           filters:
             - name: RequestRateLimiter
               args:
                 redis-rate-limiter.replenishRate: 500   # 每秒生成 500 个令牌
                 redis-rate-limiter.burstCapacity: 1000  # 桶容量 1000
                 key-resolver: "#{@userKeyResolver}"     # 按用户维度限流

每个请求进来,Gateway 会通过 Lua 脚本在 Redis 里执行一次令牌桶的扣减操作。令牌够就放行,不够就返回 429。

这个方案在业务逻辑层面没问题,但有一个前提条件:请求必须先到达 Gateway 进程,才能被限流。

Gateway 本身也会被打垮

Spring Cloud Gateway 跑在 JVM 上,底层是 Netty。单实例的处理能力大概在什么量级?

在 4 核 8GB 的机器上,Gateway 单实例能扛住的 QPS 大约在 2 万到 3 万之间,具体取决于过滤器链的复杂度和下游响应时间。

假设你配置的限流阈值是每秒 5000 个请求,正常情况下 Gateway 在自己能力范围内完成限流判断,超出的返回 429。但如果来的是突发流量呢?比如大促期间瞬间涌入每秒 10 万个请求,这些请求会怎么走?

系统架构流程图,展示从客户端到后端服务的请求处理路径。左侧为客户端图标,箭头指向 Nginx 模块;接着指向 Gateway JVM 进程模块;再向右依次是限流判断模块和后端服务模块。虚线框标注‘10 万 QPS 全部到这里’,表示网关承载全部高并发流量

所有 10 万个请求都会先打到 Gateway 进程上,Gateway 要为每个请求做以下事情:

  • 接收 TCP 连接、完成 TLS 握手
  • 解析 HTTP 请求头和请求体
  • 执行过滤器链(认证、日志、路由匹配)
  • 调用 Redis 执行令牌桶 Lua 脚本
  • 构造 429 响应并写回

就算限流判断只需要 2 毫秒,10 万个请求同时涌入,Gateway 的 Netty 线程池也扛不住这个并发量,更要命的是 JVM 还有 GC 停顿,高并发下 Young GC 可能频繁触发,每次停顿几十毫秒,请求堆积更严重。

结果就是:限流逻辑还没来得及执行,Gateway 自己先被流量压垮了。 连接队列溢出、响应超时、甚至 OOM。

这就像大门口安排了一个保安查票,但有 10 万人同时冲过来,保安还没来得及看票就被人群挤倒了。

Nginx 限流为啥这么猛

Nginx 用 C 语言写的,事件驱动 + 多进程模型,单机轻松扛住 10 万+ 并发连接,内存占用极低,一个 Nginx worker 进程处理一个请求的开销是微秒级的。

Nginx 的限流模块 ngx_http_limit_req_module 用的是漏桶算法,配置也简单:

http {
    # 定义限流规则:按客户端 IP 限流,每秒 5000 个请求,共享内存区 10MB
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=5000r/s;

    server {
        location /api/ {
            limit_req zone=api_limit burst=2000 nodelay;
            # burst=2000 允许突发 2000 个请求
            # nodelay 超出的请求立即返回 503,不排队

            proxy_pass http://gateway_upstream;
        }
    }
}

Nginx 做限流判断的成本就是一次共享内存的读写操作,不涉及任何外部调用(不需要 Redis),不涉及 JVM 和 GC,纯 C 代码在内存里算一下计数器就完事了。

同样是 10 万 QPS 打过来:

系统架构流程图,展示客户端请求经过Nginx限流判断和Gateway JVM进程最终到达后端服务的处理流程。Nginx将95000请求以503拒绝,仅放行5000请求给Gateway,突出了其在入口处的流量拦截作用

Nginx 在自己的进程内完成限流判断,95% 的请求直接返回 503,根本不会到达 Gateway,Gateway 只需要处理被放行的 5000 QPS,完全在能力范围内。

这就是 Nginx 层限流的核心价值:保护 Gateway 本身不被流量打垮。

两层限流的职责完全不同

现在能看清楚这两层限流各自负责什么。

Nginx 层:粗粒度的全局流量管控

Nginx 限流的维度通常比较粗,按 IP、按接口路径、按整体 QPS 来限,它的目标不是精确的业务限流,而是在流量入口处把洪峰削掉,确保下游系统不会被瞬间打垮。

可以理解为一道物理防线:不管你是正常用户还是恶意请求,超过阈值就直接挡回去。

Gateway 层:细粒度的业务限流

Gateway 限流可以按用户 ID、按商户 ID、按接口 + 参数组合等业务维度来限。比如:

@Bean
public KeyResolver userKeyResolver() {
    return exchange -> Mono.just(
        exchange.getRequest().getHeaders().getFirst("X-User-Id")
    );
}

按用户 ID 限流,每个用户每秒最多 100 次请求,这种细粒度的限流需要解析请求内容、读取 Header 或 Token,只有到了应用层才能做。

Nginx 拿不到用户 ID,除非你在 Nginx 层做 JWT 解析,但那样 Nginx 就变成应用服务器了,失去了轻量级的优势。

实际生产架构长什么样

稍有规模的公司,限流通常不止两层,而是三层甚至四层:

实际生产架构层级的流程图,标题为‘实际生产架构长什么样?’。从左到右展示四层架构:客户端→CDN/WAF→Nginx/OpenResty→Spring Cloud Gateway→业务服务,并配有‘层层过滤,把流量挡在前面’的总结和‘越往后成本越高’的金字塔图示。

每一层限流的位置越靠前,处理能力越强,限流粒度越粗。越靠后的层处理能力越弱,但限流粒度越细。这样每一层只需要处理上一层放行的流量,压力逐层递减。

如果只在 Gateway 层做限流,等于把所有流量压力都甩给了一个 JVM 进程。这就像一栋楼只在电梯门口装了门禁,楼门口不拦人。平时没问题,一到高峰期电梯口挤满了人,门禁系统自己先崩了。

Gateway 重启怎么办

JVM 应用发版要重启,重启期间这个实例是不处理请求的。如果限流只做在 Gateway 层,那么在 Gateway 滚动发布期间,可用实例减少,每个实例承受的压力增大,限流阈值可能需要动态调整。

Nginx 层的限流不受 Gateway 发版影响,不管后端有几个 Gateway 实例在跑,Nginx 始终按总体 QPS 控制入口流量。

还有 Gateway 的 Full GC 停顿,一次 Full GC 可能停顿几百毫秒到几秒,这期间 Gateway 无法处理任何请求,如果限流只依赖 Gateway,这段时间等于限流完全失效,Nginx 层不受影响,它在操作系统层面稳定运行。

说在最后

Spring Cloud Gateway 的限流解决的是业务层面的精细化流量管控,Nginx 的限流解决的是基础设施层面的流量保护,前者需要理解业务语义(用户、商户、接口),后者只需要简单粗暴地按 QPS 控数量。

限流的核心原则是在流量能够压垮下游之前,就在上游把它拦住。Gateway 能限流,但谁来保护 Gateway?

答案是 Nginx。


架构设计里,任何单点防护都不可靠。就像在 云栈社区 的技术讨论中常提到的一个观点:合理的系统总是在不同层级设置多重保障。把鸡蛋放在一个篮子里,迟早要出事。




上一篇:Java 21 虚拟线程能否取代 CompletableFuture?项目实践中的边界分析
下一篇:向量数据库入门:从向量原理到RAG实战,像喝水一样简单
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-7-19 22:43 , Processed in 0.701192 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表