官网时不时502,Nginx的reload会保留旧worker.......

0 阅读3分钟

一次看似玄学的 502 排查:不是浏览器问题,而是 Nginx 老 Worker 没退干净

背景

那天同事在群里扔了一句话:

“官网在 Chrome 里有时候会 502,但开无痕又正常,这到底是前端问题还是服务器问题?”

一开始我也以为是缓存、扩展、或者某个奇怪的浏览器兼容问题。结果最后发现,是一个非常典型但不太直觉的线上问题:Nginx 新旧 worker 并存,旧 worker 还在吃流量,并且持有旧配置

故障现象

  • 业务域名:https://www.***.com/home
  • 现象:
    • 普通窗口偶发 502 Bad Gateway
    • 无痕窗口“看起来更正常”
    • Ctrl+Shift+R 强刷后更容易出现 502

这个特征非常迷惑人,因为它会让人优先怀疑浏览器侧问题。

第一轮排查:先看日志

先看访问日志和错误日志,确认是不是服务端确实在返 502。

tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log

很快确认了两件事:

  1. 502 确实存在,不是前端展示问题。
  2. 触发时段里,错误日志是标准的 upstream 连接失败:
connect() failed (111: Connection refused) while connecting to upstream
upstream: "http://127.0.0.1:4000/home"
server: www.***.com

也就是:Nginx 正在把请求转发到 127.0.0.1:4000,但这个端口拒绝连接。

第二轮排查:验证 4000 端口是否真的挂了

ss -lntp | grep :4000 || echo "4000 没有在监听"
curl -I http://127.0.0.1:4000/home

输出很直接:

  • 4000 没有在监听
  • curl: (7) Failed to connect ... Connection refused

到这里已经能解释 502 的直接原因了:上游服务不在

但奇怪的是:我明明改过 Nginx 配置了

继续查当前生效配置:

nginx -T | grep -nE 'server_name www\.***\.com|proxy_pass|127\.0\.0\.1:4000'

结果更诡异:nginx -T 看到的配置里,站点代理已经不是 127.0.0.1:4000 了。

换句话说:磁盘配置是新的,但线上错误日志还在打旧 upstream

真正根因:旧 worker 没退,仍持有旧内存配置

我去看 worker 进程:

ps -eo pid,ppid,lstart,cmd | grep 'nginx: worker process' | grep -v grep

看到了“关键证据”:

  • 一部分 worker 是刚 reload 出来的新时间
  • 另一部分 worker 是很多天前的老时间(一直没退出)

这就通了:

  1. 老配置时代,站点走过 127.0.0.1:4000(SSR 方案)。
  2. 后来 Node 服务(4000)被下掉(比如 pm2 删除)。
  3. Nginx 做过 reload,但老 worker 没完全退出。
  4. 新 worker 走新配置,老 worker 走旧配置。
  5. 请求随机落到不同 worker,所以出现随机 200/502

为什么“无痕更正常、强刷更容易报错”?

这点其实是“现象”不是“根因”:

  • 无痕会有不同的缓存/连接状态,有时降低了命中失败请求的概率。
  • 强刷会绕过缓存,直接命中实时链路,更容易撞到坏 worker。

所以它看起来像浏览器问题,本质还是服务端链路不一致。

最终修复

先让老 worker 优雅退出:

kill -QUIT <old_worker_pid1> <old_worker_pid2> <old_worker_pid3>

如果还残留,再补 TERM

确认只剩新 worker 后,回归验证:

for i in $(seq 1 30); do
  curl -k -s -o /dev/null -w "%{http_code}\n" \
  --resolve www.***.com:443:127.0.0.1 \
  https://www.***.com/home
done | sort | uniq -c

结果:30 x 2000 x 502。问题消失。

顺便回答一个常见误区:pm2 都删了,为什么还有 worker?

因为它们不是一类进程:

  • pm2 管的是 Node 应用进程
  • nginx workerNginx 自己的进程

所以“pm2 删了 Node 服务”这件事,不会自动影响 Nginx worker 的生命周期。
如果 Nginx 老 worker 还活着,它仍可能按旧配置转发,继续把请求打到已下线端口。

这次事故的可复用 checklist

线上出现“偶发 502、可复现但不稳定”时,我现在会先跑这组:

# 1) 错误日志里看 upstream 指向谁
tail -f /etc/nginx/log/<site>_error.log | grep --line-buffered 'upstream\|connect() failed'

# 2) 目标上游端口是否监听
ss -lntp | grep :<port>
curl -I http://127.0.0.1:<port>/<path>

# 3) 当前配置与运行进程是否一致
nginx -T | grep -nE 'server_name|proxy_pass|<port>'
ps -eo pid,ppid,lstart,cmd | grep 'nginx: worker process' | grep -v grep

# 4) 强制本机回归测试
for i in $(seq 1 30); do
  curl -k -s -o /dev/null -w "%{http_code}\n" \
  --resolve <domain>:443:127.0.0.1 \
  https://<domain>/<path>
done | sort | uniq -c

总结

这次故障最“坑”的地方在于:
你看到的是浏览器层面的随机报错,但根因其实是 Nginx worker 代际不一致

reload只能加载新的Nginx配置,需要使用systemctl restart nginx来重启

一句话概括:
旧 worker + 旧 upstream + 已下线端口 = 随机 502。

如果你也遇到“无痕正常、强刷报错、线上随机 502”的场景,建议把“老 worker 残留”放进第一批排查项。