WebSocket 连上一会儿就断,问题出在哪?
存活时长集中在 60 秒左右,八成是反向代理的空闲超时在动手——它掐断连接时不通知任何人。
先看断开的时长特征。如果连接总是活 60 秒左右就断、客户端拿到的是 1006,基本可以锁定反向代理的空闲超时——这类代理的响应读取超时默认就是 60 秒,含义是「60 秒内没从上游读到数据就断开」。
WebSocket 协议本身不强制心跳,所以空闲连接在代理看来就是没有数据,超时到点就掐。修法有两层:把代理超时调到远大于心跳周期,同时在应用层让客户端每 25 到 30 秒发一次 ping、服务端回 pong。
WebSocket 的问题有一个很好用的诊断入口:看连接的存活时长分布。如果时长集中在一小段区间里(比如 58 到 65 秒),那通常不是业务逻辑出错,而是某一层基础设施在按固定时间回收空闲会话。
这类断开最麻烦的地方在于它是静默的:代理不通知客户端,服务端也常常收不到关闭回调,客户端只收到一个 1006——一个「什么信息都没有」的异常关闭码。所以排查的关键不是翻日志,而是先把链路分层,再逐层对照超时配置。
1先按现象定位层级
同一个「断连」现象,不同层给出的线索完全不同。
| 现象 | 最可能的层 | 进一步确认 |
|---|---|---|
| 握手返回 200 或 400 | 反向代理没透传升级头 | 检查协议版本与两个升级相关请求头 |
| 握手返回 502 | 上游服务没起或端口不对 | 在代理机器上直接连上游端口验证 |
| 连上后约 60 秒断 | 代理空闲超时到点 | 看响应读取超时是否仍是默认值 |
| 切到后台再回来就断 | 浏览器对后台页面节流 | 监听页面可见性变化,回前台主动重连 |
| 消息延迟几秒才到 | 代理缓冲了响应 | 关闭代理缓冲与缓存 |
只在 HTTPS 站点上失败时,先确认前端用的是加密的 WebSocket 协议而不是明文协议——明文在 HTTPS 页面上会被浏览器的混合内容策略直接拦掉。
2握手三件套不能少
协议升级本质上是 HTTP 的一次握手,缺任何一项都升不上去。
- 协议版本要显式声明为 1.1
- 升级依赖 HTTP/1.1,默认的 1.0 不支持。
- 升级头要透传
- 把客户端的升级请求转给上游。
- 连接头要改成升级语义
- 这一步最容易被漏,漏了就返回 200 而不是 101 切换协议。
- 别用无条件写法
- 无条件把连接头设成升级语义,会让普通请求也带上升级头,某些上游会直接拒绝;用条件映射的写法更稳。
- 验证方法
- 在服务器上直接发起一次带升级头的请求,看到 101 就说明代理这一层通了。
排错时把范围切开很重要:握手阶段就失败(200、400、502)和连上之后才断,是两个完全不同的问题,不要混在一起查。
3心跳与超时要成对设置
只调超时、不加心跳,长时间无数据时依然会断;只加心跳、不管超时,代理可能比心跳先动手。
- 客户端每 25 到 30 秒发一次 ping,服务端收到后回 pong,双方都更新最后活跃时间。
- 代理的空闲超时设成远大于心跳周期,它是兜底,不是唯一防线。
- 心跳间隔必须小于链路上最小的会话老化时间。家用路由器常见 30 到 60 秒,云负载均衡大约 90 秒。
- 有些负载均衡不允许调大空闲超时,这种情况下只能靠缩短心跳间隔来适配。
- 客户端必须监听关闭事件并实现指数退避重连,不能立即无限重试,否则会加剧服务端压力。
- 监控连接的存活时长分布:它从集中在一段区间变成平缓分布,就说明超时问题已经解决了。
顺着这个问题往下用
全部工具总览 →相关疑问
1006 到底是什么意思?
它是「异常关闭、且没有附带原因」的通用码。收到它说明连接不是走正常关闭流程收掉的,通常是传输层被中间设备掐断。具体原因不在这个码里,要结合时长特征和分层配置去反推。
为什么把代理超时调到很大,还是会断?
两种可能:一是真的在超时窗口内没有任何数据流动,兜底超时同样会生效;二是链路上还有别的层次在回收,比如网关的会话老化或负载均衡的空闲超时。这时只能靠缩短心跳间隔来解决。
服务端需要主动发心跳吗?
建议双向。服务端主动 ping 能及时探测出半开连接(客户端已断网但连接没断),避免连接池被无效连接占满。心跳的判定阈值和发送间隔要错开,否则会互相误判。
加了心跳和重连,是不是就彻底稳了?
不是。WebSocket 不保证服务端状态,重连后需要应用层自己完成状态恢复,比如重新订阅、补拉断线期间的消息。协议只负责把管道接上,不负责帮你把上下文接回来。
继续看:相关问题与资料
全部问题解答 →内容说明:本页针对具体使用问题作答,方法步骤与数值由本站自行整理与复算,事实性内容以对应官方规范与文档为准。内容最后核对日期:2026-10-08。若发现错误,欢迎通过页脚反馈入口告知。
速查在线工具箱