Nginx 反代踩坑记:一次 502 的完整排查过程

运维2026-08-02

上周五下午,业务群突然炸了:接口全在报 502。第一反应是后端挂了,登上去一看,进程活着,端口也在监听,日志里甚至还有正常请求进来。这就奇怪了。

一、先把现象固定住

复现路径很简单,curl 直接打上游没问题,走 Nginx 就 502。看 Nginx 的 error log:

connect() failed (111: Connection refused) while connecting to upstream

Connection refused?可 telnet 上游端口明明是通的。这里卡了差不多二十分钟。

二、别信 telnet,要看真实调用方

后来才想起来,Nginx 所在的那台机器开了 upstream keepalive,长连接池里缓存着一批发往旧实例的连接——上游做过一次滚动重启,旧实例的连接全成了僵尸,新连接建不起来,旧连接一用就 refused。

验证方法也简单:

ss -tnp | grep upstream_ip | awk '{print $4}' | sort | uniq -c

一堆 ESTABLISHED 挂在已经不存在的实例 IP 上,实锤。

三、修复与反思

临时方案是 reload 一次 Nginx,连接池清空立刻恢复。长期方案是给 upstream 加上合理的 idle 超时和健康检查,别让坏连接在池子里过夜:

upstream app {
    server 10.0.0.5:8000;
    keepalive 32;
    keepalive_timeout 30s;
}

这次教训:排查 502 时别只盯着"上游活没活",要看"Nginx 实际在用的那条连接活没活"。日志会撒谎,socket 状态不会。