WebSocket集群需sticky cookie保证连接粘性,因IP hash在NAT或IP变动时失效;nginx-sticky-module-ng通过route Cookie实现后端固定路由,配合后端透传Cookie即可,无需业务代码修改。

WebSocket 连接本身是长连接、无状态的,当部署在多节点集群(如 Nginx + 多个 Node.js 实例)时,客户端首次握手建立连接后,后续帧若被负载均衡器转发到其他后端节点,就会因该节点没有原始会话上下文而断连或行为异常。Sticky 模块(如 Nginx 的 ip_hash 或更精准的 sticky cookie)可解决这个问题——它不靠 IP 绑定,而是通过 Cookie 让同一客户端始终路由到同一后端,从而让 WebSocket 连接“粘”在某个实例上,实现状态局部保持。
为什么单靠 IP Hash 不够?
IP Hash 在 NAT 环境(如公司出口、4G/5G 共享 IP)下会导致多个用户被哈希到同一后端,造成资源倾斜和连接竞争;而客户端 IP 也可能动态变化(如移动网络切换)。Cookie 粘性更可靠:服务端在首次响应中写入唯一标识(如 route=abc123),后续请求携带该 Cookie,Nginx 解析后固定转发,与客户端真实 IP 无关。
配置 Nginx 的 sticky cookie(基于 upstream_hash 或第三方模块)
官方 Nginx 不直接支持 cookie-based sticky,需借助 nginx-sticky-module-ng(兼容主流版本)或使用 OpenResty + Lua 自定义逻辑。以下是基于 nginx-sticky-module-ng 的典型配置:
- 编译时加入模块:
--add-module=/path/to/nginx-sticky-module-ng - 在
http块中定义带 sticky 的 upstream:
upstream ws_backend {
sticky cookie route expires=1h domain=.example.com path=/;
server 10.0.1.10:3001;
server 10.0.1.11:3001;
server 10.0.1.12:3001;
}其中 route 是 Cookie 名,expires 控制有效期,domain 和 path 确保前端能正确携带。Nginx 会在首次响应中自动设置该 Cookie,并在后续请求中依据其值选择后端。
后端配合:WebSocket 握手阶段不干扰 Cookie 流程
Node.js(如用 ws 或 socket.io)无需主动读写 route Cookie,但需确保:
- HTTP 升级请求(Upgrade: websocket)能正常透传 Cookie 头(默认支持)
- 不覆盖或清除客户端已携带的
routeCookie(尤其在登录态中间件中) - 若使用 socket.io,禁用其自带的 session sticky(如
allowUpgrades: false配合反向代理时需谨慎)
验证方式:用 curl 模拟握手,观察响应头是否含 Set-Cookie: route=...,再带该 Cookie 重发请求,确认始终命中同一台后端(可通过日志或返回实例 ID 验证)。
注意点与替代方案
Sticky Cookie 解决的是连接路由一致性,不是跨节点状态共享。若业务需广播、房间管理或用户状态同步,仍需外接 Redis 等共享存储。另外:
- Cookie 被禁用时 fallback 到
ip_hash或least_conn,但此时无法保证 WebSocket 粘性 - 滚动发布时,旧 route Cookie 可能指向已下线节点,建议配合健康检查 + 平滑重启
- 更现代的替代:用 Kubernetes Headless Service + 客户端 DNS 缓存,或 Service Mesh(如 Istio)的 session affinity 策略
本质上,sticky cookie 是轻量、协议层透明的解决方案,适合 WebSocket 这类长连接场景,不需要修改业务代码,只要基础设施层配置得当即可生效。











