websocket原生不保证消息可靠送达,需应用层实现四层机制:唯一标识(message_id)、持久前置(收即落库)、客户端ack(即时回传)、断连上下文保留(重连续传)。

WebSocket 原生不保证消息可靠送达——send() 返回成功,只表示数据进了浏览器的 socket 缓冲区,不代表对方收到、解析或处理完成。要实现“发出去 = 对方已处理”,必须在应用层补足四层机制:唯一标识、持久前置、客户端 ACK、断连上下文保留。
给每条消息打上唯一身份证
所有业务消息必须携带全局唯一 message_id,推荐用 crypto.randomUUID()(现代浏览器支持)或 Date.now() + Math.random() 组合生成,避免纯时间戳因时钟回拨或高并发导致重复。
- 前端发送前生成 ID,并存入内存缓存(如
Map)或 IndexedDB(适合大消息或长生命周期场景) - 服务端收到后立即落库,状态设为
'received',再执行业务逻辑 - 响应体中必须原样回传该
id,前端靠它匹配原始请求 - 严禁复用 ID,否则幂等校验失效
强制客户端第一时间回 ACK
服务端推送后不能默认“已送达”,只有收到客户端带对应 message_id 的 ACK,才更新 DB 状态为 'delivered'。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 前端在
onmessage回调中一解析出id,立刻发{ type: 'ack', id: 'xxx' },不等 UI 渲染或业务处理完成 - 服务端收到 ACK 后,从待确认队列移除并更新数据库
- 若未收到 ACK,服务端按策略重发(例如 3s、6s、12s 指数间隔),并记录重试次数
连接中断时保留待确认上下文
客户端闪退、切后台、网络抖动都可能导致连接瞬间断开,未确认消息直接丢失。需主动管理发送队列和重连逻辑。
- 封装
sendMessage(msg)方法:调用前检查ws.readyState === WebSocket.OPEN,否则推入pendingQueue - 每次新建 WebSocket 实例后,在
onopen中遍历队列重发,且不清空旧队列(防止重连窗口期消息蒸发) - 服务端也需绑定用户会话(如 JWT 中的
session_id),断连时不删未 ACK 队列,重连后主动补发并重置超时计时器
补充超时与幂等防护
仅靠 ACK 不够,还需应对响应延迟、乱序、重放等边界情况。
- 前端为每条发送消息启动定时器(如 5s),超时未收 ACK 则 reject 并清理缓存;连接关闭时批量 reject 所有待处理 Promise,防内存泄漏
- 对强一致性操作(如支付、状态变更),加
seq(单调递增序列号)和idempotency_key(业务维度哈希),服务端据此做去重与顺序控制 - 服务端响应前必须先落库,避免崩溃导致消息不可追溯
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










