least_conn本身不导致websocket断开,问题在于首次分发后缺乏连接保持:需在upstream中配置keepalive、确保proxy_http_version 1.1和connection upgrade头正确透传,并避免rewrite干扰或健康检查误判引发连接迁移。

WebSocket 在 Nginx 使用 least_conn 负载均衡时断开,问题通常不在算法本身,而在于它和长连接机制的配合出了偏差。least_conn 本身只负责首次分发——把新连接(比如 WebSocket 握手请求)交给当前活跃连接数最少的后端节点。但它不保证后续帧、心跳、消息都走同一个后端。如果配置或网络链路有干扰,就可能出现“握手成功但后续通信失败”或“连接中途被重定向到其他节点”的现象。
检查 upstream 是否启用 keepalive
WebSocket 是长连接,必须复用 TCP 连接。若 upstream 没开启连接池,Nginx 每次转发帧都可能新建连接,导致状态丢失或被后端拒绝:
- 在
upstream块中显式添加keepalive 32;(数字建议 ≥ 并发连接预估量) - 确保
proxy_http_version 1.1;和proxy_set_header Connection "upgrade";已配置,否则 keepalive 不生效 - 避免在
upstream中混用max_fails/fail_timeout过于激进的设置,否则健康检查失败会误判节点下线,触发连接迁移
确认 location 匹配是否精确且无 rewrite 干扰
WebSocket 请求路径一旦被 rewrite 改写,可能导致请求头丢失、升级头被覆盖,或 Nginx 无法识别为同一连接上下文:
- 使用前缀匹配或精确匹配,例如
location /ws/ { ... },避免location / { ... }这类宽泛规则 - 禁用所有对 WebSocket 路径的
rewrite或proxy_redirect,尤其不要用rewrite ... break;或last触发内部跳转 - 确保
proxy_pass后的地址末尾斜杠与后端期望一致(如后端监听/ws,则写proxy_pass http://backend/ws;)
验证连接是否真被“跨节点转发”
WebSocket 断开未必是 least_conn 导致的,更可能是某节点异常后连接被强制重试到另一节点。需交叉验证:
- 在每个后端服务打日志,记录连接 ID + 客户端 IP + 接收的 Upgrade 头内容,对比前后两次连接是否来自同一 IP 却落到不同实例
- 用
nginx -T | grep -A5 "upstream.*backend"确认实际生效的 upstream 配置,排除多份配置覆盖 - 临时将 upstream 改为单节点(如
server 127.0.0.1:8080;),观察是否仍断开——若稳定,则说明是节点间状态不共享或健康检查误判
补充:least_conn 不适合纯 WebSocket 场景?
least_conn 是基于连接数的调度,对 WebSocket 有一定适用性,但不如 ip_hash 或 hash $remote_addr consistent; 稳定,因为后者能天然绑定客户端到固定后端:
- 若业务允许,可改用
hash $remote_addr;,避免连接漂移 - 若必须用 least_conn,务必搭配 session stickiness 的替代方案,比如后端实现连接状态中心化(Redis 存储连接元数据)
- 注意:HTTP/2 下
least_conn对 WebSocket 无效,因 HTTP/2 不支持 Upgrade,必须强制走 HTTP/1.1











