平滑重载时keepalive连接被断开并非必然,而是因worker_shutdown_timeout未配置或过短、keepalive_timeout不匹配、upstream未启用keepalive等导致旧worker强制退出;需设worker_shutdown_timeout≥30s、keepalive_timeout略小于后端超时、并启用upstream keepalive及proxy_http_version 1.1。

平滑重载(nginx -s reload)时 keepalive 连接被断开,不是“必然发生”,而是配置或环境不匹配导致的非预期行为。核心矛盾在于:旧 worker 进程在退出前,是否来得及优雅关闭空闲 keepalive 连接;新 worker 进程能否立即接管并复用连接。排查要聚焦“谁先关、怎么关、有没有漏网之鱼”。
看旧 worker 是否强制退出而非优雅关闭
keepalive 连接由旧 worker 维护,若它没等空闲连接自然超时就直接退出,就会发 RST 强制断开——这是 499、"client closed connection" 和客户端报错的主因。
- 检查
worker_shutdown_timeout是否设置:该指令控制旧 worker 等待连接关闭的最大时间(默认未启用,即不等待)。建议显式设为worker_shutdown_timeout 30s;,确保有足够窗口让 keepalive 连接自然到期或被客户端关闭 - 确认
keepalive_timeout值是否合理:若它设为 60s,而worker_shutdown_timeout只有 5s,旧 worker 很可能在连接还活着时就被杀掉 - 观察重载后 error.log 是否集中出现
recv() failed (104: Connection reset by peer)或client closed connection while waiting for request,这是旧 worker 强退的典型痕迹
查新旧 worker 连接状态是否交接异常
重载时,新 worker 启动后并不继承旧 worker 的 TCP 连接,所有连接都属于旧进程。但若旧进程未清理干净,新进程又监听同一端口,可能出现连接“悬空”或客户端复用失败。
- 重载前执行:
ss -tnp | grep :80 | grep ESTAB | wc -l记下活跃连接数 - 立即重载后 5 秒内再执行一次,对比数量是否骤降(说明大量连接被 RST);若缓慢下降且持续十几秒,说明
worker_shutdown_timeout在起作用 - 用
curl -I http://localhost配合tcpdump -i lo port 80抓包,观察重载瞬间是否有 FIN/RST 包从 Nginx 侧发出
确认 proxy 层 keepalive 是否被意外中断
如果 Nginx 作为反向代理,upstream 的 keepalive 连接也可能在重载时断开,引发后端 502 或 upstream prematurely closed connection。
- upstream 块中必须启用
keepalive N;(如keepalive 32;),否则每次请求都新建后端连接,重载时虽无影响,但性能差且掩盖真实问题 - 确保
proxy_http_version 1.1;和proxy_set_header Connection '';已配置,否则客户端传来的Connection: close会被透传,导致连接无法复用,重载时更易暴露断连 - 后端服务自身也有 keepalive 超时(如 Tomcat 的
connectionTimeout),若它比 Nginx 的keepalive_timeout还短,重载前后都可能触发断连,需同步调低 Nginx 端值
验证重载行为是否受系统限制干扰
某些系统级策略会干扰 Nginx 的优雅退出流程。
- 检查 systemd 服务配置中是否有
TimeoutStopSec设得太小(如5s),这会覆盖worker_shutdown_timeout,强制 kill 旧 worker - 运行
systemctl show nginx | grep Timeout确认实际生效的超时值 - 查看
/var/log/syslog或journald中是否有nginx: signal 15 (SIGTERM) received, exiting后紧跟着signal 9 (SIGKILL),说明被系统强杀



















