一次故障复盘会上,两个人对同一段时间的报错给出了完全相反的结论:一个坚持说后端进程崩了,另一个坚持说后端只是太慢。翻出日志才发现,那段时间里 502 和 504 同时存在。这两个码看起来都像「网关出错」,但指向的根因和处理动作完全不同。
两个码都在代理层产生
502 和 504 有一个共同点:它们都不是应用代码主动返回的,而是网关产生的。这里的网关可能是 Nginx、负载均衡、API 网关,也可能是服务网格里的 sidecar。
代理的职责是把请求转发给上游,再把上游的响应回给客户端。当代理无法从上游拿到一个「可用的响应」时,它就会自己生成一个 5xx 返回给客户端。502 和 504 正是这两种不同失败方式的名字。
理解这一点很关键:看到 502 或 504 时,应用日志里可能一个字都没有,因为请求压根没走到应用代码,或者走到了但没走完。
502:拿到了响应,但响应不可用
502 Bad Gateway 的字面意思是「网关收到了无效响应」。所谓无效,可能是上游直接断开了 TCP 连接,可能返回了一段不符合 HTTP 规范的内容,也可能是连接建立了但立刻被关闭。
最常见的几个原因:后端进程崩溃或被 OOM Killer 杀掉、后端根本没启动、上游配置的端口写错了、健康检查没通过但仍被转发、以及协议不匹配——比如把 HTTP 请求打到了只监听 HTTPS 的端口。
502 有一个很好识别的特征:返回得非常快。因为上游的连接立刻就被拒绝了,代理不需要等待。如果一批报错的响应时间都在几十毫秒以内,基本可以确定是 502。
排查方向因此也很明确:确认后端进程是否存活、端口是否在监听、upstream 配置里的地址与端口是否正确、健康检查状态是否正常。
504:一直等,等到超时
504 Gateway Timeout 是代理等待上游响应超时。这里的关键是「等待」——说明连接已经建立成功,上游也接收了请求,只是迟迟没有返回。
它的特征同样鲜明:响应耗时接近超时阈值。Nginx 的 proxy_read_timeout 默认是 60 秒,如果一批 504 的耗时都集中在 60 秒左右,就说明后端在超过阈值之后才完成或根本没完成。
根因通常在后端内部:慢 SQL 或缺失索引、调用第三方接口没有设置超时、锁等待、缓存击穿导致的数据库压力、长时间的 GC 停顿。排查顺序建议从「请求在上游的哪一段花掉了时间」入手,先看应用日志里的分段耗时,再看慢查询日志和外部调用的监控。
还要注意一个现象:偶发的 504 往往来自长尾请求。平均耗时可能只有 200 毫秒,但总有 0.1% 的请求撞上锁或者冷缓存,一跑就是几十秒。这种问题不能靠调大超时阈值解决,调大只会让慢请求占用连接更久。
为什么同一时间两种码会同时出现
上游通常是多实例部署的,不同实例的状态可能不同。部分实例因为内存溢出反复重启,就会产生 502;另一部分实例还活着但被慢查询拖住,就会产生 504。混在一起看日志,就出现了「又崩又慢」的矛盾结论。
重试机制会进一步放大这种混乱。客户端遇到 502 立刻重试,重试打到了那台慢实例上,于是同一笔业务在日志里同时留下了 502 和 504 的痕迹。
用耗时和上游日志区分它们
区分这两个码,最有效的两个证据是耗时分布和上游日志。
耗时分布刚才已经说过:502 极短,504 贴近超时阈值。这个证据在监控面板上就能直接看出来,不需要翻日志。
上游日志的规律是:502 通常在上游看不到任何访问记录,因为连接还没到应用层就失败了;504 则可能在上游留下「开始处理」但缺少「处理完成」的半截记录,或者干脆因为进程被卡住而完全没有记录。如果上游用的是同步写日志,日志写入被阻塞时,504 期间连访问日志都可能断档。
处理清单
- 看到 502:先查进程存活与端口监听,再查
upstream配置和健康检查 - 看到 504:先看应用日志的分段耗时,再查慢查询、外部依赖超时与锁等待
- 看耗时分布快速分流:毫秒级返回的多是 502,贴近超时阈值的多是 504
- 确认请求打到了哪一层,避免把网关自身的问题当成应用问题
- 不要用调大超时阈值来掩盖 504,先解决慢的来源
- 排查重试逻辑,避免重试把偶发问题放大成雪崩