
前阵子团队做技术评审新来的同事盯着架构图看了一会儿问了个很经典的问题“我们已经上了 Spring Cloud Gateway为什么架构里还要画一个 NginxGateway 不也是网关吗是不是重复了”会议室里瞬间分成两派有人觉得 Gateway 能路由、能过滤、能限流Nginx 确实多余有人觉得 Nginx 不能拿掉但追问到底“不能拿掉”的原因又说不出个所以然。这个问题几乎每个 Spring Cloud 团队都会撞上。我自己的结论先说在最前面Spring Cloud Gateway 和 Nginx 解决的是两个不同层面的问题生产环境绝大多数场景下它们是“前后配合”的关系而不是“谁替代谁”的关系。但在特定部署条件下比如云上已有负载均衡、纯内网服务Nginx 又确实可以省掉。这篇就把两者的职责边界、协作链路、省或不省的判断标准以及我在落地时踩过的坑一次讲清楚。1. 为什么会有这个困惑——两样东西表面看太像了会反复争论这个问题根源在于 Gateway 和 Nginx 从“功能清单”上看确实高度重叠。Spring Cloud Gateway 能把/user/**路由到用户服务Nginx 配置一个location /user/反代到用户服务也能干同样的事Gateway 能做负载均衡Nginx 的upstream也能做。再加上很多项目初期是单体架构最先接触的反向代理工具就是 Nginx后来微服务化引入了 Spring Cloud Gateway天然会拿它俩做对比。但这种对比忽略了一个关键维度它们所处的流量层级和解决的核心问题完全不同。我习惯用一个比喻来解释如果把微服务架构比作一个园区Nginx 是园区大门口的安保和接待处负责进门的车辆识别、访客登记、大流量疏导Spring Cloud Gateway 则是每个楼栋里的前台负责来访者进了楼之后该去几层、哪个房间、需不需要刷卡才能进某个区域。大门口的安保不解决楼内怎么走的问题楼内前台也不该替大门口去拦外面的车流。两者天然是协作关系。另一个容易混淆的点在于“网关”这个词被用滥了。Spring Cloud Gateway 官方叫 API GatewayNginx 在运维语境里也经常被叫“网关”。同一个词指的不是同一个东西。API Gateway 关注的是 API 层的治理能力比如协议转换、鉴权、灰度发布、限流熔断Nginx 关注的是接入层的流量分发和连接管理。两个层级的关注点、性能指标、故障模型完全不一样。所以纠结“有了 A 还要不要 B”不如先把各自管什么、各自擅长什么、各自对故障的容忍度是什么搞清楚。这是后面所有判断的基础。2. Spring Cloud Gateway 的真实职责——应用层的能力边界Spring Cloud Gateway 是构建在 Spring WebFlux 和 Reactor Netty 之上的响应式网关核心模型就三个概念Route路由、Predicate断言、Filter过滤器。路由定义“什么请求到哪里去”断言定义“什么条件下这条路由生效”过滤器则负责在请求转发前后做各种处理。这三样组合起来能实现的东西非常丰富。2.1 Gateway 最擅长做的事路由、断言、过滤链路由不必多说重点是它和注册中心Nacos、Eureka、Consul的联动。Gateway 可以从注册中心动态拉取服务实例列表服务上下线后路由规则实时生效不需要重启网关。这一点是 Nginx 做不到的——Nginx 的upstream里的后端地址基本是静态配置服务地址变了要靠运维手动改配置或者配合脚本 reload。断言是 Gateway 比较有特色的能力支持按照时间、Header、Cookie、Host、Method、Path、Query 等条件决定路由是否生效。比如灰度发布场景我可以写一条断言Header 里有versiongray的请求路由到灰度服务其他请求走稳定服务。这种细粒度的条件路由放在 Nginx 层做会很别扭但放在 Gateway 层就是一行配置的事。过滤器链是 Gateway 的核心价值。鉴权、IP 白名单、请求头改写、响应头改写、重试、熔断、限流全部可以通过 GlobalFilter 或 GatewayFilter 实现。团队里只要有 Java 开发能力想做什么业务相关的过滤逻辑都很方便因为过滤器就是写代码可以调用任何内部服务、查任何配置、做任何复杂的业务判断。这是 Nginx 靠 Lua 脚本或 OpenResty 才能勉强做到的事而且维护成本不在一个量级。2.2 Gateway 不太擅长也不该硬扛的事尽管 Gateway 功能很强但有几个短板是架构设计时必须正视的。第一并发连接处理和极端流量冲击能力远不如 Nginx。Netty 的异步模型本身不差但 Gateway 进程里跑的是业务过滤器链一旦某个过滤器有阻塞操作比如同步调数据库、调 Redis整个事件循环线程就会被拖住吞吐量断崖式下跌。而 Nginx 的 worker 进程模型对纯转发场景的并发承受能力极强单机扛几万并发连接是常有的事。第二Gateway 没有边缘接入层的完整能力。TLS 终止可以做但证书管理、HTTP/2 的优化、WebSocket 的长连接维持、TCP/UDP 的四层转发这些边缘能力 Gateway 要么支持得不够好要么根本不是设计目标。非要让 Gateway 全部承担代码会越来越脏性能也越来越难看。第三从故障面来看Gateway 一旦挂掉影响的是整个应用层。如果它直接暴露给外部流量意味着所有恶意扫描、攻击流量、异常请求都会直接打到应用层组件上。应用层网关被迫去处理很多它不该关心的事比如 SYN Flood、慢速连接攻击、畸形报文这些恰恰是边缘层工具更擅长防御的。3. Nginx 在这套架构里的真实位置——不是替代品是前置守门员弄清楚 Gateway 的边界之后Nginx 的位置就自然浮现出来了。在 Spring Cloud 微服务架构里我更愿意把 Nginx 定义为流量接入层而不是简单叫它“网关”。它站在整个系统的最前端和外部网络打交道Gateway 退到它后面专心做应用层路由和治理。3.1 Nginx 的看家本领连接管理、TLS 终止、静态资源先说连接管理。Nginx 采用多进程 事件驱动的异步非阻塞模型每个 worker 进程可以处理海量连接内存占用还很低。面对公网流量那堆乱七八糟的连接请求让 Nginx 在最外层消化掉大部分无效连接和握手压力比把这种压力直接释放给 Java 应用层要稳妥得多。TLS 终止是另一个典型场景。生产环境 HTTPS 证书通常统一在边缘层管理Nginx 做 TLS 卸载之后内部链路用 HTTP 明文通信或者用内部 CA 做轻量级加密。这样证书更新、过期监控、协议版本控制都集中在一个地方而不是散落在每个微服务里。Spring Cloud Gateway 也能挂证书但多个服务实例都要配证书、都要管续期运维复杂度直接翻倍。静态资源处理方面前后端分离的项目里前端打包出来的index.html、JS、CSS、图片这些静态文件Nginx 处理起来是性能最高的方案之一还能配合expires做浏览器缓存、配合 gzip/brotli 做压缩。你要是让 Gateway 去托管这些静态资源等于让一个 Java 进程干 Nginx 最擅长的脏活累活完全没有必要。3.2 Nginx 在微服务场景下的额外价值除了基础能力Nginx 在微服务架构里还能扮演几个非常实用的角色。一个是跨网关的全局流量控制。比如整个系统入口的统一 IP 黑白名单、地域封禁、按 IP 维度限流这些策略放在 Nginx 层做最合适因为它在所有业务流量之前粒度最粗但覆盖面最全。Gateway 层也可以做 IP 限流但如果有人直接绕过 Gateway 的情况出现全局策略就失效了而 Nginx 是最外层没有这个问题。另一个是健康检查和被动熔断。Nginx 的upstream可以配置max_fails和fail_timeout后端连续失败达到阈值后自动摘除节点。虽然 Gateway 自己也从注册中心感知服务健康状态但 Nginx 这一层是独立于注册中心之外的兜底——万一注册中心出现抖动或者误判Nginx 还能靠自己的探测逻辑把请求送达到真正健康的实例。再一个是多网关实例的流量分发。Spring Cloud Gateway 要做高可用通常是多实例部署前面必须有一个负载均衡器把流量分摊到各个 Gateway 实例上。这个负载均衡器可以是云上的 SLB/ALB可以是 K8s 的 Service/Ingress也可以就是一台 Nginx。如果是自建机房Nginx 做这个负载均衡是最顺手的方案配一个 upstream 指向多个 Gateway 实例地址就完事。4. 一次完整请求的分工——Nginx 和 Gateway 是如何协作的纸上谈兵没意思直接看一个生产环境的请求流转链路。假设场景是自建机房部署没有云负载均衡器前端是 Nginx Spring Cloud Gateway 多实例 Nacos 注册中心 若干个微服务。客户端 ↓ HTTPS (443) CDN / DNS 解析 ↓ Nginx (边缘层) ├── TLS 终止 ├── 静态资源处理 (前端 dist) ├── IP 黑白名单 / 基础限流 ├── HTTP 强转 HTTPS、协议版本处理 ├── upstream gateway-cluster { gateway-1:8080, gateway-2:8080, ... } └── 反向代理到 Gateway 集群 ↓ HTTP (内网) Spring Cloud Gateway (应用网关层) ├── Predicate 匹配路由 ├── GlobalFilter 鉴权 / 灰度策略 / 请求头处理 ├── 限流组件 (如 RequestRateLimiter / Sentinel) ├── 从 Nacos 动态获取服务实例 └── 转发到具体微服务实例 ↓ HTTP (内网) 业务微服务 (user-service, order-service, ...) ├── MySQL / Redis / MQ └── 响应按原链路返回在这个链路里Nginx 和 Gateway 各干各的双方都不会越界。客户端先经过 CDN 和 DNS到 Nginx 时 HTTPS 握手终止证书校验、TLS 版本协商都是 Nginx 处理完的。Nginx 判断如果是静态资源请求直接本地返回如果是 API 请求按 upstream 策略转发给某个 Gateway 实例。Gateway 拿到请求后先走过滤器链做鉴权、限流、灰度匹配然后根据路由断言找到目标服务通过负载均衡组件选一个具体实例转发过去。这里有个小细节容易被忽略Nginx 到 Gateway 的转发热门用 HTTP/1.1 并设置proxy_http_version 1.1同时把Host头、X-Forwarded-For、X-Real-IP这些头信息正确传给下一层。如果proxy_set_header Host $host丢了Gateway 里有些基于 Host 的断言会直接失效排查起来会让人怀疑人生。4.1 两层都该配置什么——职责边界表我把生产环境常见的配置项按层拆开整理成一张表团队内部新人来了我一般直接丢这张表给他们配置/能力Nginx 层Gateway 层说明TLS 证书终止负责一般不配证书集中管理避免多处维护静态资源返回负责不负责前端资源走 Nginx 直接吐IP 黑白名单负责可补充全局封禁放边缘业务级白名单放网关基础限流按 IP/连接数负责不负责挡掉恶意流量保护应用层业务鉴权JWT/OAuth不负责负责需要调用业务服务和配置放应用层路由转发到微服务不负责负责Gateway 从注册中心选实例灰度路由不负责负责按 Header/Cookie 等条件路由超时重试可配基础值负责业务级重试两层超时要协调避免 502/504访问日志负责连接层日志负责业务审计日志两层日志字段和目的不一样这张表不是绝对标准比如你非要在 Nginx 层做 JWT 验证用 OpenResty 也能做但维护成本很高。我的原则是谁能以最低成本、最高可靠性完成这个能力就放在哪一层。边缘能力往前放业务能力往后放不要为了炫技把业务逻辑堆进 Nginx。4.2 两层之间的超时和重试如何协调这块是协作里最容易出问题的地方也是我踩坑最多的地方。Nginx 转发到 GatewayGateway 转发到微服务每一层都有独立的connect timeout、read timeout、send timeout如果各层配的阈值不协调会出现一个很诡异的现象微服务一个耗时 8 秒的接口明明能正常返回客户端却收到 502 或 504。举个真实案例。之前线上一个报表接口数据量大了之后偶尔会跑 10 秒以上。Nginx 的proxy_read_timeout默认 60 秒本不该出问题但 Gateway 的HttpClient全局默认超时我没改配了 5 秒。结果就是 Gateway 自己等待后端响应超过 5 秒直接断开Nginx 收到上游异常连接给客户端返回 502 Bad Gateway。排查的时候看 Nginx 日志全是upstream prematurely closed connection一度怀疑是 Nginx 的问题后来抓包才发现是 Gateway 那层的 HttpClient 超时太短。我的建议是从客户端到边缘层、从边缘层到网关、从网关到业务服务超时时间应该逐层递减或保持一致且下游的超时不能小于上游认为正常的阈值。更稳妥的做法是慢接口尽量异步化不要在同步链路上硬扛长耗时否则总有一层会超时断开。5. 什么场景下真的可以不上 Nginx——判断标准不是“有没有”而是“缺不缺”虽然前面说了半天 Nginx 的重要性但也别形成条件反射觉得每个 Spring Cloud 项目都必须上 Nginx。在某些架构条件下Nginx 确实可以省掉而且是合理的。5.1 云上已有负载均衡器和网关产品如果你用的是公有云架构大概率是客户端流量先到云负载均衡SLB/ALB再由它转发到 Gateway 集群。云负载均衡本身已经提供了四层/七层的流量分发、健康检查、TLS 终止、DDoS 基础防护等能力这些恰恰是自建 Nginx 想解决的问题。在这种情况下再塞一台 Nginx 属于重复建设纯属给自己增加维护成本。但是注意一个细节很多云负载均衡默认只做四层转发TCP不做七层HTTPTLS 终止还得自己处理或者选择使用 ALB/HTTP 类型的负载均衡器。选型时看清楚类型不要默认“云上就不需要任何边缘配置了”。5.2 纯内网、不直接暴露公网的系统很多内部系统比如运维平台、公司内部的管理后台部署在公司内网访问链路本身就是可信网络前面没有任何公网流量压力。这种场景请求直接打到 Gateway由 Gateway 做鉴权后路由到后端服务完全可行Nginx 那层边缘防护没有发挥空间确实可以省掉。我做过几个类似的内部系统架构图里只有 Gateway 微服务跑得很干净。5.3 K8s 环境下的特殊处理在 Kubernetes 里部署 Spring Cloud 微服务时很多人会问K8s 的 Ingress 是不是可以替代 Nginx这里要区分一下概念。如果你的集群用的是 ingress-nginx它本质上是把一个 Nginx 控制器跑在集群里还是 Nginx 那套东西只不过形态变成了 K8s 资源对象来管理。如果你的集群用其他 Ingress Controller比如 Traefik、APISIX那它们承担的就是接入层职责相当于你用 Ingress 替代了传统运维体系里的 Nginx。所以不是 Nginx 不用了而是它的位置被一个形态不同的“边缘层组件”接替了。还有一种常见做法是K8s 内部 Service 直接暴露给集群内部流量完全不需要 Ingress因为每个微服务的 Service 本身就是一层负载均衡。这种模式下Gateway 部署为一组 Pod前面挂一个 Service 提供稳定的集群内访问入口也没有专门的 Nginx/Ingress 存在。这种架构在自己团队内部调试、测试环境是够用的但一旦涉及外部流量的正式环境我一般还是会建议加一层 Ingress 或独立的边缘组件。6. 实战选型清单——判断你该不该保留 Nginx把前面几章的内容压缩成一张选型判断清单你可以拿自己团队的架构逐条对照。6.1 九条判断标准场景建议理由有外部公网流量直接访问系统保留 Nginx 或云负载均衡器边缘层必须存在保护应用层全部流量来自内部可信网络可以考虑去掉 Nginx缺少公网压力边缘防护价值不大云上已有 ALB/HTTP 负载均衡可以不单独部署 Nginx云产品已覆盖边缘能力自建机房且无硬件负载均衡强烈建议保留 Nginx它是成本最低的接入层方案TLS 证书需要集中管理保留 Nginx或等价边缘组件避免证书散落在各服务Gateway 采用多实例部署必须有一个前置负载均衡器Nginx/SLB/Ingress 至少选一个服务间全走注册中心发现网关处理业务路由Gateway 承担路由Nginx 不必做业务路由分工避免配置爆炸项目是前后端分离前端资源需要托管保留 Nginx静态资源交给 Nginx 最高效安全合规要求记录访问来源、全局 IP 封禁保留 Nginx全局策略在边缘层落地6.2 我的取舍经验宁可保留不可缺失从我这些年经手的项目看真正能明确去掉 Nginx 的只有两种情况一是纯内部系统流量环境简单可控二是云上已经有成熟的七层负载均衡产品。其他情况下哪怕你在 K8s 里用 Ingress 替代边缘层这个角色也必须有组件承担而不是直接把流量怼到 Spring Cloud Gateway 上。有人觉得多一层就多一次网络跳跃延迟会增加。这个担心理论上成立但实际上 Nginx 转发到网关的额外延迟通常在毫秒甚至亚毫秒级别相对整体业务耗时几乎可以忽略。相比那一点点延迟边缘层带来的安全隔离、连接管理、故障兜底价值要大得多。不要为了省一跳而丢掉整个架构的防护层。7. 从 502 Bad Gateway 高发现象看两层的配合姿势——排障经验复盘标题里的热词里有一堆“502 bad gateway”相关的内容这不是巧合。很多团队在上了 Gateway Nginx 之后线上隔三差五出 502而且两边日志都看了还经常互相甩锅。这里我把我实际排查过的几个 502 场景和定位思路完整复盘一下希望对你有用。7.1 Nginx 日志里的502 Bad Gateway表示什么先明确一个基本语义Nginx 返回 502意味着 Nginx 作为反向代理无法从上游服务器这里的上游是 Gateway获得有效响应。也就是说问题可能出在Nginx 到 Gateway 这一段链路也可能出在Gateway 本身没有正常响应。它不一定代表 Gateway 后面的微服务出问题。排查第一步永远是看 Nginx 的 error.log关键字是upstream prematurely closed connection、connect() failed、no live upstreams。这几条分别对应不同的原因connect() failed (111: Connection refused)Gateway 端口没监听或者 Gateway 挂了。upstream prematurely closed connection while reading response header from upstreamGateway 接收请求后还没返回响应头就断开了连接。常见原因是 Gateway 内部超时、请求在过滤器链中抛异常、OOM 被杀。no live upstreams while connecting to upstreamNginx 配置里所有后端节点都处于不可用状态被主动摘除了。7.2 我踩过的典型坑一Gateway 响应太慢触发 Nginx 超时这个前面提到过。有个接口平时 200ms但偶尔遇到大数据量查询会跑到 3 秒以上。Nginx 的proxy_read_timeout当时配成了 2 秒于是每当慢请求出现Nginx 就报upstream timed out客户端收到 502。这个坑其实很好避但只要 Nginx 配置没和业务方对齐就会反复出现。我的建议是Nginx 层的超时给到一个“业务可容忍的最大耗时”再乘一个系数比如业务方承诺所有接口 3 秒内返回那proxy_read_timeout至少配 10 秒留出足够的容错空间。千万不要卡着业务耗时的线去配超时那是自己给自己挖坑。7.3 我踩过的典型坑二Gateway 线程/连接池耗尽导致连接被掐断另一种常见的 502 是 Nginx 日志显示upstream prematurely closed connection但超时时间明明够。这种情况要去看 Gateway 端的日志和监控。我遇到过一次Gateway 的下游 HttpClient 连接池默认最大连接数偏小某一个瞬间大量并发请求打进来连接池被占满新请求排队等待连接等待时间超过了某些超时阈值于是 Gateway 主动断开Nginx 就报 502。这个问题的解法分为两层一是把 Gateway 的 HttpClient 连接池适当调大二是排查为什么会有瞬间高并发——是否存在热点请求、缓存未命中、上游服务慢导致 Gateway 线程被长时间占用。只看 Nginx 日志是定位不到这一层的必须联合 Gateway 的日志和指标一起看。7.4 502 排查的标准动作清单如果你也遇到 Nginx Gateway 环境下的 502建议按这个顺序排查确认 Nginx 错误日志的类型是connect failed、timed out还是prematurely closed三种类型方向完全不同。确认 Gateway 进程是否存活直接curl一下 Gateway 的健康检查端点排除最基础的“机器挂了”问题。看 Gateway 侧日志有没有超时异常、连接池异常、OOM 记录。Spring Cloud Gateway 里可以开启访问日志和指标用于这类定位。检查网络链路Nginx 和 Gateway 之间是否有防火墙、安全组是否出现端口被临时限流。对比时间线把 Nginx error.log 的时间和 Gateway 应用日志的时间对齐找出断开的根因。上面这套流程帮我在多个项目里快速定位过问题。很多时候 502 不是配置错了而是各层组件之间的超时、连接数、负载能力不匹配需要站在整个链路的视角看待而不是只看某一层的日志。8. 个人实践里的一点体会别在这件事上搞“非此即彼”回到最初的问题Spring Cloud 体系里有 Gateway还需要 Nginx 吗我的答案是多数场景需要少数场景可以不需要但无论如何做架构设计时都要清楚每个组件存在的理由和边界。Gateway 和 Nginx 不是竞争关系而是上下游关系。Nginx 在边缘扛流量、管连接、做第一道防护Gateway 在应用层做路由、鉴权、限流和业务治理。两者各司其职配合得当整个系统才会既稳又灵活。如果你现在正处在这个选型的分岔路口我的建议很简单先想清楚你的流量从哪里来、要穿透哪几层、各层组件的故障影响面有多大。只要想清楚这几个问题技术上要不要 Nginx、用什么形态的 Nginx答案自己会冒出来。别被“有了 A 就不需要 B”这种速成经验带偏架构设计的核心永远是认清每个组件的位置而不是简单做减法。