WebSocket负载均衡关键在于维持长连接、保障会话路由与跨节点消息触达;必须透传Upgrade头、启用HTTP/1.1、设置合理超时,否则30秒内大概率断连;ip_hash适用于无状态广播,leastconn配合Redis Session更适有状态场景;K8s中优先用ClusterIP,Ingress需≥1.0版本支持upgrade透传。

WebSocket负载均衡不是简单套用HTTP的轮询策略,关键在于维持长连接不中断、保证会话可路由、消息能跨节点触达。直接上Nginx或HAProxy配置但忽略Upgrade头或超时设置,90%的概率会在30秒后断连;只做反向代理却不解决会话共享,客户端发的消息永远只在本节点生效。
为什么proxy_set_header Upgrade $http_upgrade不能少
Nginx默认把WebSocket当作普通HTTP处理,收到Upgrade: websocket请求后若不显式透传升级头,会直接返回400或降级为HTTP 1.0连接。这是最常被跳过的一步,也是连接建立失败的首要原因。
-
proxy_http_version 1.1必须显式开启,HTTP/1.0不支持Connection: Upgrade语义 -
proxy_set_header Connection "upgrade"要写死字符串,不能依赖变量(某些旧版Nginx中$http_connection可能为空) - 如果后端是Spring Boot + Tomcat,还需在
server.tomcat.connection-timeout中设为-1,否则Tomcat主动kill空闲连接
ip_hash和leastconn选哪个更稳
两者适用场景完全不同:ip_hash强制同一IP的所有连接落到固定后端,适合无状态广播架构(如所有节点都监听Redis频道);leastconn按实时连接数分发,适合有状态会话且配合共享存储(如Redis Session)的场景。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 用
ip_hash时,Nginx本身不感知后端健康状态,需配health_check或结合第三方模块,否则宕机节点仍会收流量 - 用
leastconn时,若某节点因GC暂停10秒,新连接仍会持续涌入,建议搭配max_conns限制单节点连接上限 - 生产环境更推荐
hash $remote_addr consistent(需Nginx 1.11.5+),比ip_hash抗IP漂移,比leastconn更利于连接复用
Redis Pub/Sub不是万能解,得配对用@EnableRedisHttpSession
只靠Redis发布消息,不解决会话归属问题:A节点收到消息后往Redis发,B节点收到并转发——但B节点本地根本没存这个用户的Session对象,session.getBasicRemote().sendText()会抛IllegalStateException。
- 必须启用
@EnableRedisHttpSession,让所有节点从Redis读写WebSocketSession元数据(如用户ID、连接时间) - 实际发送消息时,先查Redis确认目标用户当前连在哪台机器,再走直连或二次广播,避免盲目全量推送
- Spring Boot 3.x起
spring-session-data-redis默认用String序列化,中文key可能乱码,需自定义RedisTemplate的StringRedisSerializer
Kubernetes里Service类型选ClusterIP还是NodePort
集群内调用一律用ClusterIP,它由kube-proxy维护iptables规则,天然支持WebSocket长连接保活;对外暴露才考虑NodePort或LoadBalancer。
-
NodePort在云厂商环境下常触发SNAT,导致$remote_addr变成节点IP而非真实客户端IP,影响ip_hash准确性 - 若用Ingress(如nginx-ingress),必须确认其controller版本≥1.0,旧版不支持WebSocket的
upgrade透传 - StatefulSet部署WebSocket节点时,别用
headless Service——DNS记录不带权重,无法做健康探测,容易把流量导到未就绪Pod
真正难的从来不是配置几行Nginx,而是当一个用户在节点A登录、发了3条消息后,节点A突然OOM退出,他的连接如何被其他节点接管、历史上下文怎么恢复、未确认消息是否重发——这些细节藏在心跳检测频率、Redis过期策略、以及客户端重连退避逻辑里,而不是任何一份官方文档的显眼位置。

















