示例说明
实时行情、推送这类长连接断开是常态而非异常。裸的立即重连会在服务端故障时形成重连风暴,指数退避让间隔逐次翻倍,随机抖动错开多客户端的重连相位;心跳 ping 检测半开连接;重试上限保证彻底失联时客户端最终放弃并交由上层处理,而不是无限空转。
技术要点
- 指数退避加抖动防重连惊群
- pong 心跳检测静默断链
- closed 标志区分主动关闭与意外断开
代码解读
connect 每次创建全新 socket,open 事件把尝试计数清零并主动 ping;close 事件里先判断是否主动关闭或超出预算,再计算 2 的次幂乘基数加抖动的延迟并封顶 30 秒。close 方法置位 closed 标志并 terminate,防止优雅停机时触发多余重连。
使用建议
重连成功后要重新订阅之前的主题并请求增量补齐,弥补断线窗口的数据空洞。心跳间隔要短于网关的空闲超时。把连接状态暴露给 UI 层,用户感知比静默重试更友好。
代码示例
import WebSocket from 'ws';
function createResilientSocket(url, { maxRetries = 8, baseMs = 500 } = {}) {
let attempts = 0;
let closed = false;
let socket;
const connect = () => {
socket = new WebSocket(url);
socket.on('open', () => {
attempts = 0;
socket.ping();
socket.on('pong', () => console.log('heartbeat ok'));
});
socket.on('message', (data) => console.log('recv:', data.toString()));
socket.on('close', (code) => {
if (closed || attempts >= maxRetries) return;
const jitter = Math.random() * baseMs;
const delay = Math.min(2 ** attempts * baseMs + jitter, 30_000);
attempts += 1;
console.log('closed', code, 'retrying in', Math.round(delay), 'ms');
setTimeout(connect, delay);
});
};
connect();
return { close: () => { closed = true; socket.terminate(); } };
}
const client = createResilientSocket('wss://stream.example.com/feed');
setTimeout(() => client.close(), 60_000);