worker_shutdown_timeout 对 WebSocket 完全无效,因其仅适用于 HTTP 请求,而 WebSocket 是 Nginx 透传的长连接,不解析帧,reload 时直接 kill -9 导致帧截断和 abrupt close。

worker_shutdown_timeout 对 WebSocket 完全无效,别配它——配了也白配,还会掩盖真实问题。
为什么 worker_shutdown_timeout 在 WebSocket 场景下不起作用
Nginx 的 worker_shutdown_timeout 是为 HTTP 请求设计的:它能识别请求头结束、Content-Length 或分块传输边界,等响应发完再关连接。但 WebSocket 是长连接,Nginx 只做透传,不解析帧(比如 0x81 文本帧或 0x88 关闭帧),根本不知道“消息是否发完了”“对方是否还在收”。
所以当 reload 触发时:
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 旧 worker 收到
QUIT信号后,立刻停收新连接,但存量 WebSocket 连接只能靠内核 TCP keepalive(默认 2 小时)或客户端主动断开; -
worker_shutdown_timeout到期后不是“优雅等待”,而是直接kill -9进程,导致未发完的帧被截断,TCP abrupt close; - 日志里频繁出现
open socket left in connection和aborting,就是 socket 没 clean up 的铁证。
proxy_read_timeout 和 proxy_send_timeout 才是关键
这两个参数控制 Nginx 保持空闲 WebSocket 连接的时间上限,直接影响连接是否被误杀。它们不是“优雅下线开关”,而是“别乱断活跃连接”的守门员。
- 设太短(如默认
60s):无心跳的活跃连接会被静默关闭,客户端触发重连; - 设太长(如
7200s):旧 worker 拖太久,放大故障影响面; - 推荐值:
proxy_read_timeout 3600(1 小时),配合客户端每 30 秒发一次 ping; -
proxy_send_timeout应 ≥proxy_read_timeout,避免响应还没发完就被断; - 必须加
proxy_buffering off,否则 Nginx 缓存未发送帧,会让客户端误判连接已死。
真正平滑下线靠三方协同 draining
单靠 Nginx 配置做不到 WebSocket 优雅下线,必须后端、Nginx、客户端一起动起来:
- 后端提供
/healthz健康检查端点,下线前返回503 Service Unavailable; - Nginx 配合
proxy_next_upstream error timeout http_503,自动摘除不健康节点; - 客户端实现带退避的重连(如 1s → 2s → 4s),且关键消息带唯一 ID 做幂等;
- 运维侧用滚动更新:K8s 中设
maxSurge=0, maxUnavailable=1,Ingress Nginx Controller 自动触发连接 draining; - ALB 场景下,开启“连接优雅中断”,超时时间按业务最大处理耗时设(比如 30 秒),而非拍脑袋填 60。
最常被忽略的一点:服务端在收到 503 后,不能只等连接自然断开——要主动调用 WebSocket.CloseAsync(WebSocketCloseStatus.ServiceRestart, "restarting") 发送标准关闭帧,让客户端知道“不是网络坏了,是我在重启”,从而避免无限重连或错误提示。

















