Spring Cloud Gateway 500 问题排查
- 标签:后端、线上问题排查
🏛 业务架构师
Spring Cloud Gateway 返回 500,网关是全站入口,这个告警级别直接拉满——先看影响面再排查。我的排查按"网关自身 → 路由 → 下游"三层收敛。
第一步:从网关日志找 500 的具体原因码
Gateway 的 500 会带异常栈,常见几类:
- 下游服务异常透传:日志里是
ConnectException/ReadTimeoutException——真实故障在下游服务,网关只是背锅侠。确认目标服务实例状态。 - 路由配置错误:
NotFoundException、NoSuchRouteException——Nacos 配置变更把路由改错(路径匹配、uri 写错、谓词冲突)。配置变更是网关故障的第一嫌疑,先对配置中心的变更记录。 - 过滤器异常:自定义 GlobalFilter 抛 NPE——鉴权过滤器解析 token、改写 header 的代码 bug,新版发布引入。
- 服务发现拿不到实例:
LoadBalancer找不到可用实例——服务全挂或被摘除光(健康检查误判、发布时全批次下线)。 - 网关自身资源问题:连接池耗尽、内存不足——大量下游慢导致网关 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 一直有
高频根因(按我遇到的概率):
- 过滤器 NPE:自定义 GlobalFilter 里取 header/用户信息为 null 直接抛异常——网关层所有异常都会变 500,过滤器必须 try-catch 兜底
- 下游超时或连接被拒:下游服务挂了/注册中心摘除延迟,网关拿到
ConnectException转 500。应配置超时和熔断 fallback,把 502/504 语义化返回 - 响应体被多次写入 / Flux 序列化问题:改写响应体的过滤器(如加解密、日志)没正确处理
ServerHttpResponse,报only one connection receive subscriber allowed - 内存/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日志里能直接看到路由匹配结果、下游响应码和异常栈。
四个高频根因
- 下游服务挂了/超时:500 只是网关的包装。看日志里
ConnectException/TimeoutException,配合 Nacos 确认实例健康状态。注意 503 Bad Gateway 与 500 的区分。 - 连接池耗尽:默认 reactor-netty 连接池满后请求排队超时。症状是间歇性 500:
yaml
spring.cloud.gateway.httpclient.pool.max-connections: 500
spring.cloud.gateway.httpclient.pool.acquire-timeout: 2000- 过滤器里 NPE:自定义 GlobalFilter 对 header/参数做了假设(如取 token 没判空),异常没兜住直接 500。过滤器里 try-catch + 打点,别让断言异常裸奔。
- 响应体被截断/响应头异常:下游返回非标准内容(如 gzip 双重压缩、header 超大),netty 层报错。日志搜
DecodingException/too large(maxHeaderSize、maxResponseSize调整)。
善后 网关层必须有全局异常处理器(ErrorWebExceptionHandler)把 5xx 统一成规范 JSON 并保留 traceId;配合 SkyWalking 看网关 span 的下游明细,能少走一半弯路。