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

React接入Azure Pub-Sub的WebSocket闲置失活无法收消息如何解决

问题成因

两类机制共同导致该断连问题:

  • 浏览器后台资源节流:现代浏览器会对非活跃标签页的JS执行、网络连接做资源回收,后台标签页的WebSocket连接会被挂起甚至静默切断,且不会正常触发onclose回调,前端无法第一时间感知断连。
  • 连接超时切断:Azure PubSub 对无消息交互的空闲WebSocket连接默认45分钟左右会主动断开;同时网络链路中的NAT设备、企业防火墙通常会将10分钟以上无流量的连接从连接表中清除,这类静默断连会让前端维持“连接正常”的假状态,实际已经无法收发消息。
可行解决方案
  • 补全WebSocket全生命周期事件监听
    不要仅实现onmessage回调,必须补充onopen、onerror、onclose事件处理,一旦触发异常断连立刻执行重连逻辑。注意区分主动关闭和异常断连,只有异常断连才触发重连,重连增加1-4秒的随机延迟,避免多客户端同时重连打垮服务端。
    基础连接实现参考:
const WS_READY_STATE_OPEN = 1;
let ws = null;
const timer = {
  reconnect: null,
  idle: null,
  heartbeat: null
}

function initWebSocket() {
  // 清理旧连接和旧定时器
  Object.values(timer).forEach(t => t && clearTimeout(t));
  if (ws) {
    ws.close(1000, 'reconnect init');
    ws = null;
  }

  ws = new WebSocket(token.url);

  ws.onopen = () => {
    // 连接建立后启动心跳和空闲检测
    startHeartbeat();
    resetIdleCheck();
  };

  ws.onmessage = (event) => {
    // 原有消息处理逻辑
    // 收到任意消息都重置空闲检测计时器
    resetIdleCheck();
  };

  ws.onerror = (err) => {
    console.error('WebSocket connection error', err);
  };

  ws.onclose = (closeEvent) => {
    // 1000为主动正常关闭的状态码,其余状态码均走重连
    if (closeEvent.code !== 1000) {
      timer.reconnect = setTimeout(initWebSocket, Math.random() * 3000 + 1000);
    }
  };
}
  • 实现双向心跳保活机制
    不要依赖服务端单方面推送心跳,前端需要主动定时发送心跳包,规避服务端空闲超时和网络中间设备的连接表过期。建议心跳间隔设置为5分钟,兼容绝大多数NAT设备的10分钟超时阈值;同时设置10分钟的空闲超时阈值,连续10分钟没有收到任何消息就判定为假连接,主动断开重连。
    心跳逻辑参考:
const HEARTBEAT_INTERVAL = 5 * 60 * 1000;
const MAX_IDLE_DURATION = 10 * 60 * 1000;

function resetIdleCheck() {
  if (timer.idle) clearTimeout(timer.idle);
  timer.idle = setTimeout(() => {
    if (ws && ws.readyState === WS_READY_STATE_OPEN) {
      ws.close(4001, 'idle timeout');
    }
  }, MAX_IDLE_DURATION);
}

function startHeartbeat() {
  if (timer.heartbeat) clearInterval(timer.heartbeat);
  timer.heartbeat = setInterval(() => {
    if (ws && ws.readyState === WS_READY_STATE_OPEN) {
      // 发送心跳消息,Azure PubSub收到后会返回对应响应
      ws.send(JSON.stringify({ type: 'ping' }));
    }
  }, HEARTBEAT_INTERVAL);
}
  • 监听页面状态和网络状态变化,主动校验连接
    监听visibilitychange事件,当用户从其他标签页切回聊天页面、页面从隐藏状态变为可见状态时,第一时间检查WebSocket连接状态:如果连接不是OPEN状态直接重连;哪怕状态显示为OPEN,也主动发送一个心跳包做连通性校验,避免静默假连接。同时监听online事件,网络从断开恢复时自动重连。
    状态监听参考:
document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible') {
    if (!ws || ws.readyState !== WS_READY_STATE_OPEN) {
      initWebSocket();
    } else {
      ws.send(JSON.stringify({ type: 'ping' }));
      resetIdleCheck();
    }
  }
});

window.addEventListener('online', () => {
  initWebSocket();
});
  • React生命周期内做好资源清理
    不要在模块全局作用域直接初始化WebSocket连接,将连接初始化逻辑放在组件挂载的useEffect钩子中,组件卸载时主动关闭连接、清除所有定时器,避免多连接实例共存导致的消息混乱、内存泄漏问题。重连时如果发现用于建连的token已经过期,要先向后端请求新的WebSocket连接url,不要用过期url反复重连。
    React侧实现参考:
import { useEffect, useRef } from 'react';

export default function ChatComponent() {
  const wsRef = useRef(null);
  const timerRef = useRef({
    reconnect: null,
    idle: null,
    heartbeat: null
  });

  useEffect(() => {
    // 组件挂载时初始化连接
    initWebSocket();

    return () => {
      // 组件卸载时清理所有资源
      Object.values(timerRef.current).forEach(t => t && clearTimeout(t));
      if (wsRef.current) {
        wsRef.current.close(1000, 'component unmount');
      }
    };
  }, []);

  // 其余聊天业务逻辑
}

注意:原生WebSocket API没有内置自动重连、心跳保活能力,所有生产环境的WebSocket业务都需要自行实现上述逻辑,不要依赖浏览器或服务端的默认行为。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 21:45:38