WebSocket 连上一会儿就断,问题出在哪?

3 个讲解板块 · 关联 3 款站内工具 · 内容最后核对 2026-10-08

存活时长集中在 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心跳与超时要成对设置

只调超时、不加心跳,长时间无数据时依然会断;只加心跳、不管超时,代理可能比心跳先动手。

  1. 客户端每 25 到 30 秒发一次 ping,服务端收到后回 pong,双方都更新最后活跃时间。
  2. 代理的空闲超时设成远大于心跳周期,它是兜底,不是唯一防线。
  3. 心跳间隔必须小于链路上最小的会话老化时间。家用路由器常见 30 到 60 秒,云负载均衡大约 90 秒。
  4. 有些负载均衡不允许调大空闲超时,这种情况下只能靠缩短心跳间隔来适配。
  5. 客户端必须监听关闭事件并实现指数退避重连,不能立即无限重试,否则会加剧服务端压力。
  6. 监控连接的存活时长分布:它从集中在一段区间变成平缓分布,就说明超时问题已经解决了。

相关疑问

1006 到底是什么意思?

它是「异常关闭、且没有附带原因」的通用码。收到它说明连接不是走正常关闭流程收掉的,通常是传输层被中间设备掐断。具体原因不在这个码里,要结合时长特征和分层配置去反推。

为什么把代理超时调到很大,还是会断?

两种可能:一是真的在超时窗口内没有任何数据流动,兜底超时同样会生效;二是链路上还有别的层次在回收,比如网关的会话老化或负载均衡的空闲超时。这时只能靠缩短心跳间隔来解决。

服务端需要主动发心跳吗?

建议双向。服务端主动 ping 能及时探测出半开连接(客户端已断网但连接没断),避免连接池被无效连接占满。心跳的判定阈值和发送间隔要错开,否则会互相误判。

加了心跳和重连,是不是就彻底稳了?

不是。WebSocket 不保证服务端状态,重连后需要应用层自己完成状态恢复,比如重新订阅、补拉断线期间的消息。协议只负责把管道接上,不负责帮你把上下文接回来。

内容标识:本页内容由人工智能辅助生成,并经人工核对与编辑。详见 人工智能生成内容标识说明。
内容说明:本页针对具体使用问题作答,方法步骤与数值由本站自行整理与复算,事实性内容以对应官方规范与文档为准。内容最后核对日期:2026-10-08。若发现错误,欢迎通过页脚反馈入口告知。