WebSocket断线消息补发与时序对齐需应用层主动设计:依赖bufferedAmount识别未发出消息,用唯一messageId+待确认队列+本地持久化实现重发;重连后先拉取离线消息再补发,服务端须快速ACK、按ID去重、离线缓存;时序以serverSeq为准,clientSeq仅作临时排序。

WebSocket断线期间的消息补发和时序对齐,不是靠重连自动恢复的,而是必须由应用层主动设计机制来保障。连接断开时,前端已调用send()但未真正抵达服务端的消息,浏览器不会帮你追回;重连成功后,若不干预,新旧消息极易乱序、重复或丢失。
断线前未发出消息的识别与暂存
关键依据是socket.bufferedAmount:它反映浏览器输出缓冲区中尚未真正发出的字节数。在onclose或onerror触发时,若该值大于 0,说明有消息卡在队列里,从未离开客户端。
- 应在每次
send()后立即为消息生成唯一messageId(推荐crypto.randomUUID()),并存入待确认队列(如Map) - 监听
onclose事件,在其中遍历待确认队列,把bufferedAmount > 0对应的消息标记为“待重发”,并持久化到localStorage或IndexedDB(防止页面刷新丢失) - 避免依赖时间戳+随机数拼接ID,存在碰撞风险,会导致服务端去重失效
重连后的消息补发与幂等控制
重连成功(onopen触发)不等于状态同步完成。此时要分两步走:先拉取离线消息,再补发本地未确认消息——顺序不能反,否则可能造成“先收到新消息、后收到旧消息”的倒序。
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
- 重连后第一件事:发送
{ "type": "fetch_offline", "lastSeq": 12345 },其中lastSeq是客户端已处理的最新消息序列号(可从本地存储读取) - 服务端返回离线消息列表,每条含
messageId和内容;客户端逐条处理,并对每条响应标准ACK - 离线消息处理完毕后,再从本地待发队列中取出未确认消息,按原始顺序重发;每条重发前检查是否已在服务端被处理(比如通过
messageId查缓存),避免重复提交
服务端的配合要点
客户端机制再完善,没有服务端协同也形同虚设。服务端需承担三类责任:快速ACK、按ID去重、离线缓存。
立即学习“前端免费学习笔记(深入)”;
- 收到任意业务消息,立刻返回
{"type":"ack","messageId":"xxx"},不要等数据库写入完成后再发ACK - 所有写入操作前,先用
messageId查Redis或DB,若已存在则直接返回ACK,不重复执行业务逻辑 - 用户离线时,将消息存入带TTL的缓存(如7天),key建议为
userId:messageId;重连拉取时按时间或分页返回,防止单次载荷过大
时序对齐的核心原则
真实场景中不存在全局统一时钟,所以不能依赖时间戳排序。可靠时序只能靠两个锚点:客户端本地递增序列号 + 服务端落库后的权威序列号(如DB自增ID或分布式seq)。
- 客户端发送时携带
clientSeq(本地单调递增整数),便于前端渲染时做临时排序 - 服务端处理成功后,在ACK或后续广播消息中附带
serverSeq,客户端据此更新本地最大已知序号 - 当离线消息与重发消息混杂时,以
serverSeq为准排序;若暂无serverSeq(如刚重发还未ACK),先按clientSeq暂排,收到ACK后再调整


















