如何实现WebSocket可靠重连?解决代理场景下连接恢复问题
WebSocket代理场景下的重连优化需求
我有一个通过WebSocket与Python服务器通信的Web应用,需要实现服务器可用性检测及指定IP/端口端点的自动重连功能。
当前实现逻辑
- 尝试建立Socket连接,失败则关闭Socket并间隔500ms重试
- 通过浏览器的
on_error和on_close回调检测连接失败 - 本地部署场景下,用
setTimeout实现1500ms的连接超时逻辑
遇到的问题
使用TCP代理时出现异常:
- 关闭服务器能被快速检测到
- 重启服务器后,UI无法立即检测到连接恢复,甚至需要30秒以上,期间多次触发错误回调
测试过的代理情况
- VSCode端口转发:存在已知Bug,可靠性差
- SSH端口转发(
-L参数):表现相对较好,但等待时间越长,连接恢复耗时越久
开发环境
我在Linux虚拟机上开发服务器,需要用Windows浏览器访问,因此必须使用代理。
核心代码
create_socket(): void { const that = this this.close_socket() // Only closes if not already closed this.reconnect_timeout_handle = null this.socket = new WebSocket("ws://" + this.hostname + ":" + this.port) this.socket.onmessage = function (e) { that.on_socket_message_callback(e.data) } this.socket.onclose = function (e) { that.on_socket_close_callback(e) } this.socket.onopen = function (e) { that.on_socket_open_callback(e) } this.socket.onerror = function (e) { that.on_socket_error_callback(e) } // My homemade timeout this.connect_timeout_handle = setTimeout( function () { if (that.socket !== null) { if (that.socket.readyState != that.socket.OPEN) { that.close_socket() } } } as Function, this.connect_timeout // 1500ms ) } on_socket_open_callback(e: Event): void { this.clear_connect_timeout() // App specific code removed on purpose } clear_connect_timeout(): void { if (this.connect_timeout_handle != null) { clearTimeout(this.connect_timeout_handle) this.connect_timeout_handle = null } } on_socket_error_callback(e: Event): void { this.clear_connect_timeout() this.close_socket() // Closes only if not already closed if (this.enable_reconnect) { this.try_reconnect(this.reconnect_interval) // reconnect_interval = 500 } } try_reconnect(delay_ms: number): void { // Register a callback for a connect attempt in x msec const that = this if (this.reconnect_timeout_handle == null) { this.reconnect_timeout_handle = setTimeout( function () { that.create_socket() } as Function, delay_ms ) } } on_socket_close_callback(e: CloseEvent): void { this.clear_connect_timeout() if (this.socket !== null) { this.socket = null } if (this.enable_reconnect) { this.try_reconnect(this.reconnect_interval) } }
优化方案
1. 重连策略:指数退避+随机抖动
固定500ms重试会导致代理/服务器重启后瞬间涌入大量连接请求,反而触发延迟或限流。改成指数退避+随机抖动,避免重试风暴:
// 新增变量 private retryCount = 0; private maxReconnectDelay = 10000; // 最大重试间隔10秒 try_reconnect(): void { if (this.reconnect_timeout_handle) return; // 计算延迟:基础间隔*2^重试次数(不超过最大值)+ 0-1秒随机抖动 const delay = Math.min( this.reconnect_interval * Math.pow(2, this.retryCount), this.maxReconnectDelay ) + Math.random() * 1000; this.retryCount++; this.reconnect_timeout_handle = setTimeout(() => { this.create_socket(); this.reconnect_timeout_handle = null; }, delay); } // 连接成功后重置重试计数 on_socket_open_callback(e: Event): void { this.clear_connect_timeout(); this.retryCount = 0; // 业务代码... }
2. 主动心跳检测
代理可能维持无效连接状态,通过主动心跳确认服务器可用性:
private heartbeatInterval: number | null = null; private heartbeatTimeout: number | null = null; private readonly heartbeatDelay = 5000; // 每5秒发一次心跳 private readonly heartbeatTimeoutDelay = 3000; // 3秒无响应则判定断开 on_socket_open_callback(e: Event): void { this.clear_connect_timeout(); this.retryCount = 0; this.startHeartbeat(); // 业务代码... } startHeartbeat(): void { this.stopHeartbeat(); this.heartbeatInterval = setInterval(() => { if (this.socket?.readyState === WebSocket.OPEN) { this.socket.send(JSON.stringify({ type: "heartbeat" })); // 设置心跳超时检测 this.heartbeatTimeout = setTimeout(() => { this.close_socket(); this.try_reconnect(); }, this.heartbeatTimeoutDelay); } }, this.heartbeatDelay); } stopHeartbeat(): void { if (this.heartbeatInterval) clearInterval(this.heartbeatInterval); if (this.heartbeatTimeout) clearTimeout(this.heartbeatTimeout); this.heartbeatInterval = null; this.heartbeatTimeout = null; } // 处理心跳响应 on_socket_message_callback(data: any): void { const message = JSON.parse(data); if (message.type === "heartbeat_ack") { if (this.heartbeatTimeout) clearTimeout(this.heartbeatTimeout); this.heartbeatTimeout = null; } // 其他业务逻辑... } // 关闭连接时停止心跳 close_socket(): void { this.stopHeartbeat(); // 原有关闭逻辑... }
注意:需要Python服务器配合,收到heartbeat消息后回复heartbeat_ack
3. 修复连接超时逻辑
当前超时逻辑存在重复操作风险,优化后仅在连接中状态下处理:
create_socket(): void { this.close_socket(); this.reconnect_timeout_handle = null; this.socket = new WebSocket(`ws://${this.hostname}:${this.port}`); // 用箭头函数简化上下文绑定 this.socket.onmessage = (e) => this.on_socket_message_callback(e.data); this.socket.onclose = (e) => this.on_socket_close_callback(e); this.socket.onopen = (e) => this.on_socket_open_callback(e); this.socket.onerror = (e) => this.on_socket_error_callback(e); this.connect_timeout_handle = setTimeout(() => { if (this.socket?.readyState === WebSocket.CONNECTING) { this.socket.close(1000, "Connection timeout"); this.socket = null; if (this.enable_reconnect) this.try_reconnect(); } }, this.connect_timeout); }
4. SSH代理参数优化
使用SSH端口转发时,添加存活检测参数,避免连接闲置断开:
ssh -L 本地端口:服务器IP:服务器端口 -o ServerAliveInterval=10 -o ServerAliveCountMax=3 用户名@虚拟机IP
ServerAliveInterval=10:每10秒发送一次存活检测ServerAliveCountMax=3:连续3次无响应则断开连接,触发本地重连
内容的提问来源于stack exchange,提问作者user2302957
相关产品推荐
相关产品推荐

