52-useWebSocket自动重连

52-useWebSocket:自动重连与消息分发

实时待办推送的前端侧------112行的composable管连接/重连/消息分发/生命周期。退避重连(3秒起步30秒封顶)、handler异常隔离、组件卸载自动断开。这是第43篇推送链路的浏览器终点。

文章目录

源码:browise-vue/src/core/useWebSocket.ts(112行)

消费方:MessageList.vue(消息中心)、HomeDemo.vue(全局挂载)

服务端:NotificationWebSocketHandler(第43篇)


一、112行的API面

typescript 复制代码
const ws = useWebSocket(userId)
ws.connect()                          // 建连
ws.onMessage((data) => { ... })       // 订阅消息
ws.disconnect()                       // 主动断开(不重连)
// 响应式状态
ws.connected    // ref<boolean>------连接状态(可绑定UI小圆点)
ws.lastMessage  // ref<WsMessage|null>------最新消息

消息契约 ------{type:'workflow_message', userId, procInstId, msg, timestamp}------type字段区分消息类别(workflow_message/notice_published)------一个连接多类消息靠type路由


二、URL构造的两个细节

typescript 复制代码
function getUrl(): string {
  const proto = location.protocol === 'https:' ? 'wss:' : 'ws:'    // ①协议跟随
  const uid = userId || JSON.parse(localStorage.getItem('browise_user_info')).psnId
  return `${proto}//${location.hostname}:8080/ws/notification?userId=${uid}`
}

①wss/ws跟随页面协议 ------https页面里开ws://会被浏览器拦(mixed content)------协议必须自适应

②userId的两级来源 ------参数显式传 > localStorage的用户信息------组件可以不传userId (挂载时自动从登录态取)。userId在query string不在header------WebSocket浏览器API不能自定义Header------后端Handler从query解析(这是WebSocket鉴权的通行妥协------query会被日志记录,生产环境该升级为连接后首条消息鉴权)。

硬编码的:8080 ------开发环境直连后端端口。生产(同域)会出问题 ------location.hostname:8080在生产可能不通------该用location.host(含端口自适应)或可配置的baseURL。E2E测试全过是因为测试环境就是8080------已知的部署适配点


三、退避重连:3秒起步30秒封顶

typescript 复制代码
let reconnectAttempts = 0
let shouldReconnect = true

function scheduleReconnect() {
  if (reconnectTimer) clearTimeout(reconnectTimer)
  if (!shouldReconnect) return               // 主动断开不重连
  reconnectAttempts++
  const delay = Math.min(30000, 3000 * reconnectAttempts)   // 3s,6s,9s...30s封顶
  reconnectTimer = setTimeout(() => connect(), delay)
}

ws.onopen = () => { reconnectAttempts = 0 }   // 连上重置计数

指数不是指数是线性 ------3s×次数的线性增长(不是2^n的指数)------网络抖动秒级恢复的场景3/6/9秒已经够用 ,指数退避(3/6/12/24)在服务端故障时更保守------政务内网的选择:线性够

shouldReconnect标志位 ------disconnect()设false------用户主动退出登录后的onclose不触发重连 。没有这个标志:登出→连接关闭→3秒后自动重连→已登出用户又连上了(userId还在URL里)------安全事故。

30秒封顶的理由 ------重连风暴防护。网关重启时几千个客户端同时重试------无限增长的延迟只会让客户端长时间失联------封顶让重试节奏稳定,服务端恢复后最多30秒全员回来。


四、handler异常隔离

typescript 复制代码
ws.onmessage = (event) => {
  const data: WsMessage = JSON.parse(event.data)
  lastMessage.value = data
  handlers.forEach(h => {
    try { h(data) } catch (e) { console.error('[WS] 消息处理异常', e) }
  })
}

每个handler独立try-catch ------一个订阅者抛异常不影响其他订阅者、不断连接。消息中心组件的bug不连累待办推送------多订阅者场景的健壮性底线。

JSON.parse在消息层 ------协议定型为JSON文本帧------解析失败只console不重连(坏消息不该触发连接重置)。


五、生命周期:onUnmounted自动清理

typescript 复制代码
onUnmounted(() => {
  disconnect()
})

组件卸载自动断开 ------Vue3的composable里注册组件级钩子------useWebSocket在哪个组件setup里调用,组件销毁时就断。避免"路由切走连接还挂着"的僵尸连接累积。

全局推送的用法 ------HomeDemo.vue(主框架)调用一次------主框架不销毁连接一直在 ;MessageList.vue(消息中心页)也可以订阅------页面级订阅随页面销毁。一个连接,两级订阅生命周期。


六、与第43篇的完整闭环

复制代码
后端completeTask
  → WorkflowNotifyService.sendMessage
     → 消息入库(WORKFLOW_MESSAGE)
     → afterCommit → publishEvent
        → WebSocketEventListener
           → notificationWebSocketHandler.sendToUser(userId, json)
              → userId的Session集合逐个send
                 ↕ WebSocket连接
              → 浏览器ws.onmessage
                 → handlers分发
                    → MessageList加一条 / ElMessage弹通知

推送失败的用户 ------下次打开消息中心getUnreadMessages拉库------推+拉双通道的拉侧兜底(第43篇)。


✅ 亮点:wss/ws协议自适应、userId两级来源、线性退避30秒封顶的政务内网选择、shouldReconnect防登出自动重连事故、handler异常隔离、onUnmounted自动断连、硬编码8080的部署适配点。适合做实时推送前端的人。扩展方向:第43篇事务后推送、第42篇消息表兜底、第53篇dict缓存。

相关推荐
青山木1 小时前
RocketMQ 入门到原理(一):整体架构与消息的生命周期
java·分布式·后端·中间件·架构·rocketmq
xcl09251 小时前
上海24小时自助健身房系统软件开发实战与架构解析
java·spring boot
cAuth1 小时前
实现一个图形编辑器
前端·架构·计算机图形学
haishikeji696_1 小时前
飞控管理平台源码|无人机智慧巡查系统私有化部署 低空一网统管巡检平台选型指南
架构·无人机·低空经济·无人机巡检·无人机管理系统·一网统飞·飞控管理平台
安易算力1 小时前
昇腾生态开发深度实践:CANN算子库架构解析与MindSpore模型优化
网络·容器·架构·kubernetes·vllm
Huanzhi_Lin1 小时前
从0搭建ECS:跨品类评估
架构·游戏开发·ecs·ecs跨品类评估
张宜强1 小时前
023 SpringBoot实现萌宠集市
java·课程设计
花哥码天下1 小时前
docker流水线
java
十二同学啊2 小时前
Drools规则引擎:让复杂业务规则从代码中解耦
java·后端·开源