6月1日 02:13

WebSocket 断线重连怎么实现?指数退避+心跳完整方案

WebSocket 有四个连接状态:CONNECTING(0)OPEN(1)CLOSING(2)CLOSED(3)。断线重连的核心是在 onclose 回调中判断关闭原因,对异常关闭自动重建连接。基础实现需控制重连次数上限,避免无限重试消耗资源。重连间隔采用指数退避策略,初始延迟如 1 秒,每次翻倍,上限 30 秒,避免短时间内大量重连请求冲击服务端。同时要区分正常关闭(code 1000)和异常关闭,正常关闭不应触发重连。心跳检测是重连机制的重要补充,客户端定时发送 ping,若 pong 超时未返回则主动关闭连接触发重连。还需监听浏览器的 online/offline 事件,离线时暂停重连,恢复在线后再尝试连接。完整方案应包含:指数退避间隔、最大重连次数、心跳检测、优雅关闭判断、离线状态感知、连接状态通知上层、断线期间消息队列缓存。

javascript
class ReconnectWebSocket { constructor(url) { this.url = url; this.reconnectCount = 0; this.maxReconnect = 5; this.heartbeatInterval = null; this.heartbeatTimeout = null; this.messageQueue = []; this.connect(); } connect() { this.ws = new WebSocket(this.url); this.ws.onopen = () => { this.reconnectCount = 0; this.startHeartbeat(); this.flushQueue(); }; this.ws.onclose = (e) => { this.stopHeartbeat(); if (e.code !== 1000) this.reconnect(); }; this.ws.onmessage = (e) => { if (e.data === 'pong') { clearTimeout(this.heartbeatTimeout); return; } this.onMessage(e.data); }; } reconnect() { if (this.reconnectCount >= this.maxReconnect) return; const delay = Math.min(1000 * Math.pow(2, this.reconnectCount), 30000); setTimeout(() => { this.reconnectCount++; this.connect(); }, delay); } startHeartbeat() { this.heartbeatInterval = setInterval(() => { if (this.ws.readyState === WebSocket.OPEN) { this.ws.send('ping'); this.heartbeatTimeout = setTimeout(() => this.ws.close(), 5000); } }, 30000); } send(data) { if (this.ws.readyState === WebSocket.OPEN) { this.ws.send(data); } else { this.messageQueue.push(data); } } }

追问

为什么用指数退避而不是固定间隔重连?

固定间隔在服务端故障时会产生大量同步重连请求(雷群效应)。指数退避让重连间隔逐步增大,分散请求压力。加上随机抖动(jitter)效果更好:delay = min(base * 2^n + Math.random() * 1000, 30000),避免多个客户端同一时刻同时重连。

哪些关闭码不应该触发重连?

关闭码 1000 表示正常关闭,1001 表示端点离开(如页面关闭),都不应重连。需要重连的是异常关闭码:1006(异常关闭,无关闭帧)、1002/1003(协议错误)、1011/1012(服务端异常/重启)、1013(稍后重试)。判断逻辑:code !== 1000 && code !== 1001 时触发重连。

心跳检测和重连怎么配合?

心跳负责主动发现"静默断连"——TCP 连接已断但 onclose 未触发。客户端每隔 30 秒发 ping,服务端回 pong,5 秒内未收到 pong 则判定连接已死,调用 ws.close() 主动关闭,触发 onclose 再进入重连流程。没有心跳,客户端可能长时间不知道连接已断。

离线检测怎么实现?

监听 window.addEventListener('online', reconnect)window.addEventListener('offline', stopReconnect)。离线时暂停重连定时器,避免无效请求;恢复在线后立即尝试连接,并重置退避计数。比单纯靠 onclose 重连更及时。

断线期间的消息怎么处理?

维护一个消息队列,连接断开时将待发送消息入队而非丢弃。重连成功后(onopen 触发)按顺序发送队列中的消息。对于实时性要求高的场景,重连后可主动请求服务端补发缺失数据,队列只作为兜底。

标签:WebSocket