Nginx仅作反向代理,不管理WebSocket状态;需通过会话保持绑定连接、Redis存储连接元数据、消息中间件实现广播,并由后端协同处理生命周期。

Nginx 本身不负责、也无法同步 WebSocket 的连接状态。它只是一个反向代理,只做连接转发和协议透传,所有状态(比如谁连在哪个节点、Session 是否活跃、用户所属房间)都必须由后端应用层自己管理。
真正要解决“集群中状态同步”问题,核心思路是:让 Nginx 稳住连接路由,把状态同步和消息分发交给业务服务自己完成。
一、先让 Nginx 不拆散连接(会话保持)
WebSocket 是长连接,一旦被 Nginx 轮询打到不同后端,就会出现“连在 A 节点,消息发到 B 节点”的丢消息问题。所以第一步是强制绑定:
ip_hash
简单有效,适合公网 IP 分布较均匀的场景。同一 IP 始终落到同一台后端。hash $http_sec_websocket_key;
更精准——每个 WebSocket 握手请求都有唯一Sec-WebSocket-Key,用它哈希可保证一次连接的所有帧(包括 ping/pong)都走同一节点。Sticky cookie(OpenResty / Nginx Plus)
后端握手成功后 Set-Cookie(如ws_route=node-2),Nginx 用sticky cookie ws_route实现路由。适合移动端或经 CDN/NAT 的环境。
⚠️ 别用
least_conn或默认轮询,这是集群下 WebSocket 消息丢失的最常见原因。
二、连接状态不能靠 Nginx,得存到共享存储
WebSocket Session 是 JVM 内存对象,无法跨进程序列化。你不能把它塞进 Redis 然后“复活”,但可以存它的元数据:
- 用户 ID、连接 ID、所属节点标识(如
node-1)、上线时间、房间号、登录态等轻量信息 - 存到 Redis(建议用 Hash 或 String + TTL),各节点均可读写
这样当用户 A 要给用户 B 发消息时:
- 服务先查 Redis,知道 B 当前连在
node-2 - 再通过内部 RPC、HTTP 调用或消息队列,把消息投递给
node-2 -
node-2从本地内存中取出 B 的真实 Session,完成推送
三、广播类消息(如系统通知、群聊)必须走中间件
Nginx 不支持广播,也不能让节点互相直连调用(易雪崩、难运维)。推荐两种成熟路径:
Redis Pub/Sub
所有后端节点订阅同一个频道(如ws:global:msg)。发广播时往频道 publish,每个节点收到后,只推送给本节点管理的在线连接。简单、低延迟,适合中小规模。Kafka / RabbitMQ
更可靠,支持重试、回溯、分区。适合高吞吐、强一致性要求场景(如金融行情、工单广播)。需配合消费组 + 连接归属查询使用。
✅ 关键逻辑:广播 ≠ 全节点盲推;而是“查归属 → 定向通知 → 本节点内投递”。
四、连接生命周期要协同处理
- 客户端断连时,本节点需及时清理本地 Session,并同步更新 Redis 中的状态(如删 key 或设 offline flag)
- 其他节点可通过定时扫描或监听 Redis Key 失效事件感知掉线
- 客户端应实现带退避的自动重连(避免瞬时重连风暴),重连时重新做身份校验和连接注册
Nginx 层也要配合:proxy_read_timeout 设为大值(如 86400),禁用缓冲(proxy_buffering off),防止空闲时被静默断开。
Nginx 是管道,不是大脑。状态同步这件事,它既没能力管,也不该管。真正起作用的是你后端服务怎么设计连接注册、元数据存储、消息路由和故障恢复这整套机制。











