You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 20:35:09