websocket断开后需靠心跳探测、指数退避、资源清理和消息缓存实现可靠重连:用业务心跳替代原生ping/pong,20秒发ping、3秒未收pong即断连;重连延迟为min(1000×2^attempts,30000),上限5–10次;每次重连前清除定时器、事件监听器并置空ws实例;断连期间消息入队,重连成功后重发。

WebSocket 连接断开后不能靠 onclose 一触发就立刻新建实例,因为中间设备(如 Nginx、CDN、运营商 NAT)常静默断连,onclose 根本不执行,readyState 还可能卡在 1(OPEN)状态,导致“假连”——发不出消息却毫无察觉。真正可靠的自动重连,必须结合心跳探测、指数退避、资源清理和消息缓存四部分。
用业务心跳代替原生 Ping/Pong
浏览器 WebSocket API 不暴露发送底层 Ping 帧的接口,必须用业务消息模拟心跳。前后端约定好格式,例如客户端每 20 秒发 {"type":"ping"},服务端收到后立即回 {"type":"pong"}。
- 心跳间隔要小于链路中最短空闲超时:Nginx 默认
proxy_read_timeout=60s,移动网关常见 30s,CDN 可能是 45s,所以推荐设为 20 秒发 ping,3 秒未收 pong 即判定失效 - 每次发 ping 前先清掉上一次的
setTimeout,避免多个超时同时触发 - 收到 pong 后重置心跳计时器;超时则主动调用
ws.close()并进入重连流程
实现带指数退避的重连控制器
重连不是越快越好,盲目轮询会压垮客户端和服务端。应采用指数退避策略:失败次数越多,等待时间越长,但有上限。
- 延迟计算公式:
delay = Math.min(1000 * Math.pow(2, attempts), 30000)(即 1s → 2s → 4s → 8s → 16s → 最大 30s) - 最大重试次数建议设为 5–10 次,超过后可降级为手动重连或提示用户检查网络
- 使用
setTimeout启动下一次连接,不要用setInterval,防止定时器堆积 - 连接成功后,重置失败次数、清除所有定时器,并重新启动心跳
每次重连前必须彻底清理旧连接
残留资源是内存泄漏和逻辑错乱的主因。新连接建立前,务必做以下清理:
- 调用
clearTimeout(this.heartbeatTimeout)和clearInterval(this.pingInterval) - 移除旧
WebSocket实例的所有事件监听器(推荐用具名函数定义onmessage,再显式removeEventListener) - 将旧
ws实例置为null,避免后续误用 - 进入重连状态时,忽略新的重连请求(比如用户狂点“重连按钮”)
断连期间发送的消息不能丢
用户操作产生的消息,在连接不可用时不能直接报错或丢弃,而应暂存队列,等重连成功后再批量重发。
- 封装一个
send(data)方法,内部判断ws.readyState === WebSocket.OPEN,否则推入this.sendQueue = [] - 重连成功触发
onopen后,遍历队列逐条发送,并清空队列 - 对关键消息可加唯一 ID 和重发标记,避免服务端重复处理
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











