nginx默认proxy_pass会断开websocket连接,因其1.3.13前不识别upgrade头,且默认proxy_read_timeout仅60秒;必须显式配置http/1.1、透传upgrade和connection头,并延长超时、禁用缓冲。

WebSocket长连接本身不支持传统HTTP的请求级负载均衡,直接套用轮询或随机分发会导致连接中断、会话丢失、消息乱序——这不是配置错了,是协议层就不兼容。
为什么Nginx默认proxy_pass会悄悄断开WebSocket连接
Nginx在1.3.13之前完全不识别Upgrade: websocket头,即使新版默认开启代理,若缺少关键头传递,后端根本收不到升级请求,会当作普通HTTP 400拒绝。更隐蔽的问题是:Nginx默认proxy_read_timeout为60秒,空闲WebSocket连接会被静默关闭,客户端收不到任何通知。
- 必须显式设置
proxy_http_version 1.1 - 必须透传两个关键头:
proxy_set_header Upgrade $http_upgrade和proxy_set_header Connection "upgrade" - 把
proxy_read_timeout调到至少300秒(或设为0表示永不超时),同时后端也要配对应心跳间隔 - 避免使用
proxy_buffering on——WebSocket二进制帧被缓冲后可能延迟送达甚至粘包
HAProxy里leastconn和ip_hash到底该选哪个
选leastconn还是ip_hash,取决于你的业务是否依赖会话状态。如果每个连接只做匿名广播(如行情推送),leastconn能真正平衡负载;但如果用户登录态存在内存中(比如聊天室成员列表),就必须用ip_hash,否则换节点后会话丢失。
评估 Kubernetes 集群安全态势,覆盖 RBAC、工作负载安全、网络策略、基础设施即代码(IaC)、运行时监控和密钥管理等 30 项控制项……
-
ip_hash在IPv6或经过多层NAT时可能失效,此时应改用cookie insert方式维持会话 -
leastconn对突发连接友好,但无法解决单个连接长期占用导致的“连接倾斜”——某个用户建了50个Tab,全落在同一台机器上 - 健康检查必须用
option httpchk GET /health而非TCP层检测,否则无法感知应用层挂死
async-http-client连不上HAProxy后端的常见原因
Java客户端用async-http-client连HAProxy时,90%的失败不是代码问题,而是TLS握手或超时配置不匹配。
- HAProxy若启用了
ssl绑定但没配verify none,客户端必须信任其证书链,否则连接直接被Netty底层拒绝 -
AsyncHttpClientConfig里的setConnectTimeout要大于HAProxy的timeout connect,否则还没完成TCP握手就被客户端主动放弃 - 若HAProxy配置了
timeout tunnel 1h,客户端setReadTimeout也得同步设长,否则读操作抛java.util.concurrent.TimeoutException - 别忽略
setMaxRequestRetry——WebSocket握手失败时重试次数为0是默认值,需手动设为≥1
Kubernetes Service暴露WebSocket时最易忽略的点
K8s的Service类型为ClusterIP或NodePort本身不干预连接状态,但Ingress控制器(如Nginx Ingress)若没启用WebSocket支持,所有流量仍按HTTP处理。
- Ingress资源必须加注解:
nginx.ingress.kubernetes.io/upgrade: "websocket" - 不要依赖
SessionAffinity: ClientIP——K8s的ClientIP在云厂商LB后可能全是同一个内网IP - StatefulSet比Deployment更适合WebSocket服务,因为Pod名固定,便于日志追踪和连接数统计
- HPA扩缩容时,旧Pod上的连接不会自动迁移,必须配合优雅终止:
preStop钩子里发送close帧并等待30秒
真正难的不是让WebSocket连上负载均衡器,而是让连接在故障转移、滚动更新、网络抖动中不丢状态——这要求心跳帧、重连退避、会话存储三者严格对齐,漏掉任意一环,高可用就只剩字面意思。










