Vue3 + TS 企业级 WebSocket 封装实战:高可用、自动重连、心跳检测与业务解耦方案

Vue3 + TS 企业级 WebSocket 封装实战:高可用、自动重连、心跳检测与业务解耦方案

一、 前言

在实时性要求极高的应用场景(如:消防接处警中心、实时监控大屏、即时通讯等)中,WebSocket 是不可或缺的核心技术。然而,在实际开发中,简单的 new WebSocket(url) 远不能达到生产环境的要求。

我们需要面对以下挑战:

  • 稳定性:网络抖动、代理服务器超时导致的连接断开。
  • 僵死连接:客户端显示已连接,但实际上已无法接收消息。
  • 业务解耦:如何让多个页面/组件方便地监听不同的实时数据流。
  • 重连策略:如何优雅地进行重连,既要保证及时性,又要避免对服务器造成冲击。

本文将结合生产代码,深度解析一个企业级的 WebSocket 封装方案。


二、 核心技术实现 (websocket.ts)

1. 设计模式:单例模式

为了确保整个应用中只维护一个长连接,我们采用了单例模式。这样无论在哪个组件中引用,操作的都是同一个连接对象。

typescript 复制代码
class WebSocketService {
  // ... 私有属性
}

// 导出单例
const wsService = new WebSocketService(server.value);
export default wsService;

2. 精准的双向心跳检测 (Heartbeat Mechanism)

为什么需要心跳检测?因为在长连接中,可能会出现"半打开连接"的情况:客户端认为连接正常,但中间网络设备(如防火墙、代理服务器)已将其切断。

我们的方案采用了 "主动探测 + 超时判定" 的双定时器策略:

(1) 双定时器逻辑
  • heartbeatTimer (定期任务) :类似于"脉搏"。每隔 30s 触发一次,负责向服务端发送一个 ping 消息。
  • serverTimeoutTimer (安全监控) :类似于"哨兵"。每当发出 ping 之后,都会启动这个定时器。如果在预设的 15s 内没有收到服务器的任何回应,哨兵就会判定连接已死,强制执行重连。
(2) 为什么是"双向"?

虽然是由客户端发起的 ping,但它能验证双向通路:

  1. 上行通路 :客户端能成功发出 ping
  2. 下行通路 :服务端收到 ping 后返回 pong,或者发送了业务数据。注意: 只要客户端收到服务器发来的任何消息(包括 pong 或业务数据),都会触发 resetHeartbeatTimeout(),重置"哨兵"定时器。
(3) 代码深度实现
typescript 复制代码
/**
 * 启动心跳:开启定期发送逻辑
 */
private startHeartbeat(): void {
  this.stopHeartbeat(); // 清理旧定时器,防止叠加
  this.heartbeatTimer = setInterval(() => {
    if (this.connected) {
      this.sendHeartbeatPing();
    }
  }, this.options.heartbeatInterval);
  
  // 首次连接成功后立即发送一次,建立即时感知
  this.sendHeartbeatPing();
}

/**
 * 发送 Ping:触发上行探测
 */
private sendHeartbeatPing(): void {
  try {
    this.ws.send('ping'); // 框架约定的纯文本心跳
    this.resetHeartbeatTimeout(); // 开启下行监控
  } catch (error) {
    console.error('发送心跳失败:', error);
  }
}

/**
 * 重置超时:只要收到消息,就说明链路是通的
 */
private resetHeartbeatTimeout(): void {
  // 每次收到消息(onmessage)时都会调用此方法
  if (this.serverTimeoutTimer) {
    clearTimeout(this.serverTimeoutTimer);
  }
  
  // 开启一个延迟任务,如果 heartbeatTimeout 时间内没被再次重置,则说明服务器失联
  this.serverTimeoutTimer = setTimeout(() => {
    console.warn('WebSocket 心跳响应超时,主动关闭连接以触发重连');
    if (this.ws) {
      this.ws.close(); // 触发 onclose 回调,进入 handleReconnect 逻辑
    }
  }, this.options.heartbeatTimeout);
}

3. 高鲁棒性的自动重连机制

重连机制是 WebSocket 长连接的"生命线"。在移动端网络切换、电梯信号屏蔽或服务器短暂重启等场景下,自动重连能显著提升用户体验。

(1) 触发时机

我们的封装在以下三种情况下会自动触发重连:

  • onclose 事件:正常或异常的连接关闭。
  • onerror 事件:建立连接失败或通信过程中发生错误。
  • 心跳超时 :通过 serverTimeoutTimer 判定连接已成为"僵尸连接"时,主动关闭连接从而触发重连。
(2) 指数退避算法 (Exponential Backoff)

为了防止在服务器宕机时,大量客户端同时高频请求导致"雪崩效应",我们引入了指数退避策略。

  • 逻辑:每次重连失败,下一次尝试的延迟时间都会翻倍。
  • 配置项
    • reconnectDelay: 初始延迟(2s)。
    • maxReconnectDelay: 最大延迟上限(60s)。
    • maxReconnectAttempts: 最大重连次数(设置为 -1 表示无限重连,确保系统最终能自愈)。
typescript 复制代码
private handleReconnect(): void {
  // 1. 状态锁:如果已连接或正在连接,则跳过
  if (this.connected || (this.ws && this.ws.readyState === WebSocket.CONNECTING)) return;

  // 2. 检查重连次数上限
  if (this.options.maxReconnectAttempts === -1 || this.reconnectAttempts < this.options.maxReconnectAttempts) {
    this.reconnectAttempts++;
    
    // 3. 计算延迟:delay * 2^(n-1) -> 2s, 4s, 8s, 16s...
    const backoffDelay = Math.min(
      this.options.reconnectDelay * Math.pow(2, Math.max(0, this.reconnectAttempts - 1)),
      this.options.maxReconnectDelay
    );

    console.log(`WebSocket 将在 ${backoffDelay / 1000}s 后进行第 ${this.reconnectAttempts} 次重连`);

    this.reconnectTimer = setTimeout(async () => {
      try {
        await this.connect(); // 尝试重新建立连接
      } catch (error) {
        // connect 内部已处理错误,此处只需记录
        console.error('本次重连尝试失败');
      }
    }, backoffDelay);
  } else {
    ElMessage.error('网络连接已断开,请手动刷新页面');
  }
}
(3) 状态复位

重连成功后,必须及时重置状态,以便下一次故障时重新开始计算延迟:

  • onopen 回调中将 reconnectAttempts 清零。
  • 清除现有的 reconnectTimer

4. 环境感知:网络与可见性

  • online 监听:当电脑从断网恢复联网时,立即尝试重连。
  • visibilitychange 监听:当用户切回浏览器标签页时,主动检查连接状态。
typescript 复制代码
private initGlobalListeners(): void {
  window.addEventListener('online', () => {
    this.reconnectAttempts = 0; // 重置次数,立即尝试
    this.connect();
  });
  window.addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'visible' && !this.connected) {
      this.connect();
    }
  });
}

5. 消息分发机制 (Topic-based)

通过 Map<string, MessageHandler[]> 维护消息处理器。当后端发来不同 type 的消息时,自动分发给注册了该 type 的回调函数。

typescript 复制代码
on(type: string, handler: MessageHandler): void {
  if (!this.messageHandlers.has(type)) {
    this.messageHandlers.set(type, []);
  }
  this.messageHandlers.get(type)?.push(handler);
}

三、 业务层实战应用 (index.vue)

在接处警中心页面中,我们需要实时接收:新告警通知、被接单同步、接单超时通知。

1. 挂载与订阅

在组件挂载时注册对应的消息处理器。

typescript 复制代码
onMounted(async () => {
  // 初始化基础数据
  await getReceivingCenterPageList();

  // 注册 WebSocket 消息处理器
  wsService.on('alarmInfo', processAlarmData);           // 新告警
  wsService.on('acceptOrder', handleAcceptOrderMessage);  // 订单已被接
  wsService.on('alarmInfoTimeOut', handleAcceptOrderTimeoutMessage); // 订单超时

  // 监听连接状态用于 UI 反馈
  wsService.onConnectionChange(handleConnectionChange);

  // 建立连接
  wsService.connect().catch(e => console.error(e));
});

2. 实时数据更新逻辑

以新告警为例,收到消息后直接将数据插入响应式数组的头部,并加上 newFlag 配合 CSS 动画(如呼吸灯效果)。

typescript 复制代码
const processAlarmData = (alarmData) => {
  const data = JSON.parse(alarmData);
  if (data.alarmLevel === 1) {
    // 插入列表首位,触发前端动画
    redAlarms.value.unshift({ ...data, newFlag: true });
    redTotal.value++;
  }
};

3. 资源清理

在组件销毁时注销回调,避免内存泄漏或逻辑重复执行。

typescript 复制代码
onUnmounted(() => {
  wsService.off('alarmInfo', processAlarmData);
  wsService.off('acceptOrder', handleAcceptOrderMessage);
  wsService.offConnectionChange(handleConnectionChange);
  // 注意:单例模式下不宜直接调用 disconnect(),因为其他页面可能还在使用
});

四、 核心优势总结

  1. 高可用性:通过双向心跳和多重重连机制,最大程度保证连接在线。
  2. 鉴权优化 :使用 RefreshToken 建立连接。由于 WebSocket 建立后难以像 HTTP 那样方便地刷新 Access Token,使用 Refresh Token 能显著提升长连接的稳定性。
  3. 零入侵业务:封装后的服务通过事件订阅工作,页面无需关注 WebSocket 的连接、关闭、重连等底层细节,只需关注数据处理。
  4. 性能友好:指数退避重连算法和单例设计模式,有效保护了客户端性能和服务器压力。

五、 代码参考

相关推荐
淼淼爱喝水1 小时前
DVWA 文件上传漏洞实验%00 截断实验与.htaccess 文件攻击实验
网络·安全·靶场
omenkk71 小时前
网络IO模型-从BIO到IO多路复用
服务器·网络
xhbh6661 小时前
内网端口映射外网完全教程:路由器端口转发配置+内网穿透工具实战
服务器·网络·智能路由器·端口映射·映射
x***r1511 小时前
Wireshark 4.4.2 Intel 64.dmg Mac 安装教程|附抓包权限设置
网络
广州灵眸科技有限公司1 小时前
瑞芯微(EASY EAI)RV1126B 模型部署API说明
linux·开发语言·网络·人工智能·深度学习·算法·yolo
9命怪猫1 小时前
公司内部网络计费参考
网络
柿柿快乐1 小时前
用户认证系统实现文档
linux·服务器·网络·学习·基础教学
皮皮虾12341 小时前
龙珠激斗多开自动挂机搬砖攻略教程
网络·游戏
北京盟通科技官方账号1 小时前
【技术深挖】EtherCAT 如何实现超高精度同步?深度解析分布式时钟 (DC)
网络协议·数据传输·ethercat·智能工厂·盟通科技·windows实时·ec-master