WebSocket消息可靠送达需应用层实现“发出去+收ACK+超时重试”机制:每条消息带唯一message_id,客户端发送后存入待确认队列并启动定时器,超时未收ACK则重发(限3次);接收方解析成功后立即发ACK;服务端用Redis记录pending ID,收到ACK后移除并更新状态。

WebSocket 本身不保证消息“被对方业务层处理”,conn.send() 成功只代表数据进了内核缓冲区,不代表前端收到、解析成功或用户看到。要确保送达,必须在应用层补全确认逻辑——核心是“发出去 + 收到回执 + 超时重试”。
给每条消息加唯一 ID
这是整个机制的基础。没有唯一标识,就无法追踪、去重、匹配 ACK。
- 用
crypto.randomUUID()(现代浏览器)或Date.now() + Math.random()生成客户端侧 message_id - ID 需随消息体一并发送,例如:
{"type":"chat","message_id":"a1b2c3","content":"你好"} - 服务端收到后不要修改该 ID,原样透传给接收方,也用于后续 ACK 匹配
客户端发送后进入等待 ACK 状态
不是发完就不管,而是主动维护一条“待确认消息队列”。
- 调用
ws.send()前,把消息对象(含 ID、内容、时间戳、重试次数)存入内存 Map 或 IndexedDB - 启动一个定时器(如 5 秒),超时未收到对应 ACK 就触发重发
- 重发时更新重试次数,避免无限循环;达到上限(如 3 次)后标记为“失败”,可提示用户或写入本地日志
接收方收到后立即发 ACK
ACK 不是可选动作,是协议约定的义务,且越快越好(不能等页面渲染完再发)。
立即学习“Java免费学习笔记(深入)”;
- 在
onmessage回调里,第一时间判断是否为业务消息(排除 ping/pong) - 成功解析 JSON 后,立刻构造 ACK 消息:
{"type":"ack","message_id":"a1b2c3"},并ws.send() - 即使后续业务逻辑报错(比如 DOM 找不到、状态更新失败),ACK 仍要发出——因为“收到并识别”已完成
服务端配合完成闭环
客户端只是半边,服务端需记录、匹配、更新状态,否则 ACK 无意义。
- 服务端推送消息给目标用户时,把 message_id 写入 Redis Set,键如
pending:uid_123 - 收到客户端 ACK 后,从该 Set 中移除对应 ID,并更新数据库中该消息的 status 为
delivered - 可搭配定时任务扫描超时未 ACK 的 ID(比如 30 秒未确认),触发告警或补偿流程
不复杂但容易忽略:消息 ID 必须全局唯一、ACK 必须由接收方主动发出、超时重试必须有退避和上限。这三环缺一不可。


















