WebSocket心跳与断线重连
背景
WebSocket 连接看起来是“长连接”,但在真实网络环境里会被 NAT、网关、负载均衡、移动网络切换等因素中断。没有心跳和重连,页面会表现为“看似在线,实际上数据已断流”。
目标
构建一套可落地策略:
- 快速发现连接失活
- 避免重连风暴
- 在网络抖动时保证用户体验
心跳设计
服务端与客户端职责
- 客户端:定时发送
ping(或业务心跳包) - 服务端:及时返回
pong - 客户端:维护“最后一次收到消息时间”
推荐参数
- 心跳间隔:20~30 秒
- 超时阈值:2~3 个心跳周期
- 断线判定:超过阈值未收到任何消息(不仅仅是 pong)
如果业务本身消息很频繁,可降低心跳频率,避免无效流量。
重连策略
指数退避 + 抖动
不要固定 1 秒重连。建议:
- 第 1 次:1 秒
- 第 2 次:2 秒
- 第 3 次:4 秒
- 上限:30 秒
- 加随机抖动:
delay * (0.8 ~ 1.2)
这样可以避免服务恢复时大量客户端同时打满连接入口。
何时停止重连
- 用户主动退出登录
- 页面明确进入只读离线模式
- 服务端返回不可恢复错误(鉴权失败等)
会话恢复
重连成功后不要只看“连接成功”,还要补齐状态:
- 重新鉴权(token 可能已过期)。
- 重新订阅频道/房间。
- 通过消息序号补拉断线期间数据。
如果没有消息序号,至少记录“最近一次时间戳”进行增量拉取。
监控指标
要把 WebSocket 当作一条生产链路监控:
- 在线连接数
- 每分钟断开数
- 重连成功率
- 平均重连耗时
- 心跳超时占比
当“断开数升高 + 重连成功率下降”同时出现时,通常是服务端或网络侧故障。
常见坑
- 只实现
onclose重连,没处理“连接假活” - 心跳包仅前端发,服务端不回
- 重连后忘记重新订阅,导致“连上了但没数据”
- 多标签页重复建连,造成资源浪费
总结
WebSocket 稳定性靠的是机制而不是运气。心跳负责“快速发现问题”,重连负责“平滑恢复”,会话恢复负责“业务一致性”。三者缺一不可。