Skip to content

Spring Cloud Gateway 500 问题排查 ​

  • 标签:后端、线上问题排查

🏛 业务架构师 ​

Spring Cloud Gateway 返回 500,网关是全站入口,这个告警级别直接拉满——先看影响面再排查。我的排查按"网关自身 → 路由 → 下游"三层收敛。

第一步:从网关日志找 500 的具体原因码

Gateway 的 500 会带异常栈,常见几类:

  1. 下游服务异常透传:日志里是 ConnectException / ReadTimeoutException——真实故障在下游服务,网关只是背锅侠。确认目标服务实例状态。
  2. 路由配置错误:NotFoundException、NoSuchRouteException——Nacos 配置变更把路由改错(路径匹配、uri 写错、谓词冲突)。配置变更是网关故障的第一嫌疑,先对配置中心的变更记录。
  3. 过滤器异常:自定义 GlobalFilter 抛 NPE——鉴权过滤器解析 token、改写 header 的代码 bug,新版发布引入。
  4. 服务发现拿不到实例:LoadBalancer 找不到可用实例——服务全挂或被摘除光(健康检查误判、发布时全批次下线)。
  5. 网关自身资源问题:连接池耗尽、内存不足——大量下游慢导致网关 IO 线程堆积。

第二步:快速恢复手段

  • 配置类问题:配置回滚(所以网关路由变更必须走灰度 + 可秒级回滚)
  • 代码类问题:回滚网关版本
  • 下游问题:切降级页面 / 限流保护下游

架构师的预防观点

  • 网关变更要有独立发布窗口和审批,它不是普通业务服务,一次错误配置 = 全站 500
  • 网关侧按路由维度打点(路由命中率、下游耗时、错误率),500 告警要能直接下钻到"哪条路由的哪个下游"
  • 自定义过滤器必须有兜底 try-catch,过滤器异常绝不能升级成网关 500,失败语义应该是明确的 401/403
  • 大促前做网关配置全量巡检 + 故障演练(杀下游实例验证熔断降级)

🏗 系统架构师 ​

Spring Cloud Gateway 报 500,我的排查思路是"先定位是网关自己错,还是网关代理的下游错",一层层收窄。

第一步:拿到具体证据

  • 网关日志搜异常栈。SCG 基于 WebFlux,同步异常会包装成 ErrorResponseException,日志里通常有根因
  • 看响应体/日志里有没有下游状态码线索:下游返回 4xx/5xx 被网关包装、下游直接断连(Connection reset by peer)都会在网关侧表现为 500
  • 区分面:偶发 vs 全量,单个路由 vs 所有路由,发布后才有 vs 一直有

高频根因(按我遇到的概率):

  1. 过滤器 NPE:自定义 GlobalFilter 里取 header/用户信息为 null 直接抛异常——网关层所有异常都会变 500,过滤器必须 try-catch 兜底
  2. 下游超时或连接被拒:下游服务挂了/注册中心摘除延迟,网关拿到 ConnectException 转 500。应配置超时和熔断 fallback,把 502/504 语义化返回
  3. 响应体被多次写入 / Flux 序列化问题:改写响应体的过滤器(如加解密、日志)没正确处理 ServerHttpResponse,报 only one connection receive subscriber allowed
  4. 内存/Metaspace:路由数多、动态刷新路由频繁导致 Metaspace 涨满,GC 异常引发各种诡异 500

治理动作:全局 ErrorWebExceptionHandler 统一兜底返回结构化错误并记 traceId;为每个路由配置 response-timeout 与熔断;关键链路把网关指标(5xx 比例、路由级 RT)接监控告警。核心一句话:先分类(网关自身 vs 下游传递),再拿异常栈定位,最后补过滤器兜底与超时语义。

💻 全栈开发专家 ​

Spring Cloud Gateway 的 500 排查,我先把日志调成 debug 看清转发细节,再按四个高频根因排查。

第一步:打开诊断日志

yaml
logging:
  level:
    org.springframework.cloud.gateway: debug
    reactor.netty: debug

日志里能直接看到路由匹配结果、下游响应码和异常栈。

四个高频根因

  1. 下游服务挂了/超时:500 只是网关的包装。看日志里 ConnectException/TimeoutException,配合 Nacos 确认实例健康状态。注意 503 Bad Gateway 与 500 的区分。
  2. 连接池耗尽:默认 reactor-netty 连接池满后请求排队超时。症状是间歇性 500:
yaml
spring.cloud.gateway.httpclient.pool.max-connections: 500
spring.cloud.gateway.httpclient.pool.acquire-timeout: 2000
  1. 过滤器里 NPE:自定义 GlobalFilter 对 header/参数做了假设(如取 token 没判空),异常没兜住直接 500。过滤器里 try-catch + 打点,别让断言异常裸奔。
  2. 响应体被截断/响应头异常:下游返回非标准内容(如 gzip 双重压缩、header 超大),netty 层报错。日志搜 DecodingException / too large(maxHeaderSize、maxResponseSize 调整)。

善后 网关层必须有全局异常处理器(ErrorWebExceptionHandler)把 5xx 统一成规范 JSON 并保留 traceId;配合 SkyWalking 看网关 span 的下游明细,能少走一半弯路。