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

WebSocket重连代码引发资源占用过高的原因及调试方案

WebSocket闲置后CPU/内存暴涨问题分析与解决

代码中的明显问题

  • 未清理旧WebSocket实例导致内存泄漏:每次重连时直接覆盖全局SOCKET变量,但旧的WebSocket实例及其绑定的事件监听器仍会被浏览器持有,无法被垃圾回收。多次休眠唤醒后,会堆积大量无效的socket实例,直接导致内存占用飙升。
  • 固定间隔重连无退避机制:socket断开后每隔5秒强制重连,若服务器持续无法连接,会不断创建新的socket连接,加剧内存消耗,甚至触发服务器限流后进入恶性循环。
  • setInterval未适配页面休眠状态:电脑休眠时浏览器定时器会暂停,唤醒后可能一次性触发所有堆积的定时器回调,短时间内大量执行日志输出或socket请求,引发CPU占用过高。

调试步骤

  • 内存泄漏排查:打开Firefox开发者工具「内存」面板,分别在页面刚加载、闲置1小时、唤醒后各拍一次内存快照,对比WebSocket对象的数量,若数量持续增长即可确认实例堆积问题。
  • CPU占用定位:在CPU飙升时,用「性能」面板录制30秒左右的性能数据,查看调用栈中哪个函数(如socket事件回调、定时器函数)占用了大量CPU时间。
  • 重连次数监控:在startSocket函数开头添加console.log('重连次数:', ++reconnectCount)(需先声明全局reconnectCount=0),观察是否存在短时间内大量重连的情况。
  • 页面可见性测试:添加页面可见性监听,打印定时器触发时机,确认是否在唤醒后集中执行大量回调。

修复方案

1. 清理旧Socket实例+优化重连逻辑

每次重连前主动关闭旧socket并解除事件绑定,同时添加指数退避重连机制:

var SOCKET = null;
var reconnectCount = 0;
var heartbeatTimer = null;

function startSocket() {
    // 清理旧实例
    if (SOCKET) {
        SOCKET.removeEventListener('open', onSocketOpen);
        SOCKET.removeEventListener('close', onSocketClose);
        SOCKET.removeEventListener('error', onSocketError);
        if (SOCKET.readyState !== WebSocket.CLOSED) {
            SOCKET.close(1000, '主动清理旧连接');
        }
        SOCKET = null;
    }

    SOCKET = new WebSocket('wss://[REDACTED]');
    SOCKET.addEventListener('open', onSocketOpen);
    SOCKET.addEventListener('close', onSocketClose);
    SOCKET.addEventListener('error', onSocketError);
}

// 独立事件回调,方便移除监听
function onSocketOpen(event) {
    document.getElementById('disconnected-overlay').style.display = 'none';
    reconnectCount = 0;
    scheduleHeartbeat();
}

function onSocketClose(event) {
    console.log(`websocket closed: ${event.code} (${event.reason})`);
    document.getElementById('disconnected-overlay').style.display = 'block';
    // 指数退避:第一次5秒,第二次10秒,最多60秒
    const delay = Math.min(5000 * Math.pow(2, reconnectCount), 60000);
    setTimeout(startSocket, delay);
    reconnectCount++;
    clearHeartbeat();
}

function onSocketError(event) {
    console.log(`websocket error: ${event}`);
    document.getElementById('disconnected-overlay').style.display = 'block';
    const delay = Math.min(5000 * Math.pow(2, reconnectCount), 60000);
    setTimeout(startSocket, delay);
    reconnectCount++;
    clearHeartbeat();
}

// 用setTimeout替代setInterval,避免定时器堆积
function scheduleHeartbeat() {
    heartbeatTimer = setTimeout(() => {
        console.log("4 minutes");
        if (SOCKET?.readyState === WebSocket.OPEN) {
            SOCKET.send(JSON.stringify({
                "user_name": "debugging",
                "change": { "AddMinutes": 0 },
            }));
            // 仅在socket正常时继续调度下一次心跳
            scheduleHeartbeat();
        } else {
            console.log("socket was not open");
        }
    }, 240000);
}

function clearHeartbeat() {
    if (heartbeatTimer) {
        clearTimeout(heartbeatTimer);
        heartbeatTimer = null;
    }
}

function mainSetup() {
    startSocket();
    // 监听页面可见性,休眠时暂停心跳
    document.addEventListener('visibilitychange', () => {
        if (document.hidden) {
            clearHeartbeat();
        } else {
            if (SOCKET?.readyState === WebSocket.OPEN) {
                scheduleHeartbeat();
            }
        }
    });
}

2. 适配页面休眠状态

通过visibilitychange事件监听页面是否可见,休眠时暂停心跳定时器,唤醒后根据socket状态恢复,避免唤醒后批量执行回调。


内容的提问来源于stack exchange,提问作者Violet

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 17:54:58