websocket消息排序需客户端发消息时带单调递增client_seq(本地持久化管理),服务端生成权威server_seq,前端按server_seq滑动窗口归并消费,并在重连时先同步last_server_seq再恢复。

WebSocket 通信中实现基于序列号的消息排序,核心是**在应用层引入严格递增的 client_seq,并由服务端生成权威 server_seq,前端统一按 server_seq 归并和消费**。协议本身不保证业务消息的逻辑顺序,只保证单连接内帧有序,所以排序必须自己做,不能依赖网络或浏览器。
客户端发消息时带单调递增 client_seq
每次发送前从本地持久化存储(如 localStorage)读取并自增,再存回,确保跨页面刷新、多 tab 场景下不重复、不跳变:
- 不要用 Date.now()、UUID 或 Math.random()——它们无法表达先后关系
- 起始值建议为 1,不是 0;避免负数或非整数
- 多个 tab 共享同一 WebSocket 连接时,需用 BroadcastChannel 或 IndexedDB 同步计数器状态,防止 seq 冲突
服务端返回结构化 ACK 并推送带 server_seq 的最终结果
收到消息后立刻响应 ACK(含原 client_seq),不等业务处理完成;真正落库或撮合成功后,再推送含 server_seq 的结果消息:
- server_seq 必须来自数据库主键、分布式 ID 或全局单调序列服务(如 Twitter Snowflake),不可由客户端提供
- ACK 和最终消息都携带相同 client_seq,便于前端映射状态
- 所有业务写入必须幂等:用 client_seq + 消息体哈希查 Redis 缓存,已存在则跳过执行
前端用滑动窗口缓存 + nextExpected 推进消费
不把所有消息塞进数组再 sort,而是用 Map + 指针方式低开销重排:
- 维护固定大小窗口(如 512),Map 存 {server_seq → message},key 可用 server_seq % windowSize 防止内存无限增长
- 记录 nextExpected(当前期望收到的最小 server_seq),收到消息后先丢弃已处理过的(seq
- 收到 nextExpected 后,持续 pop 并处理,同时递增 nextExpected,直到断档
- 若 nextExpected 缺失超 2.5 秒,主动发 {"type":"resend","from":nextExpected,"to":nextExpected} 请求补推
重连时必须先同步 last_server_seq 再恢复
断线重连后,不能直接重发未确认消息,否则旧请求结果可能晚于新请求结果到达:
- 重连成功后第一件事:发 {"type":"fetch_offline","last_server_seq":12345},向服务端拉取该序号之后的所有消息
- 服务端按 server_seq 升序返回离线消息列表,前端逐条写入本地有序缓冲区
- 等离线消息全部 confirmed 后,再从待重发队列中按原始 client_seq 顺序重发未 ACK 消息
- localStorage 中需同时持久化 last_server_seq 和最后成功发送的 client_seq,避免重连后序号回退
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











