答GavinGeng
不丢和不重其实是两个机制,别想用一条通道同时解决。我一般分三层做:
第一层,给每条消息一个按用户维度单调递增的全局序列号 msg_seq,客户端本地记「已收到最大 seq」。推送下来发现中间缺号(比如收到 101、103,没有 102),就主动拉补。
第二层,ACK 别逐条确认,用滑动窗口上报「我已确认到 seq=N」。连接断开重连时带上 last_seq,服务端把 [N+1, 当前] 这段补发一遍,在线态就续上了。
第三层,离线消息单独落存储(有序列表或消息队列都行),按 seq 持久化。重连后先补离线区间再切回在线,保证离线期间也不丢。
去重靠消息唯一 msg_id + 客户端本地去重表,重复推来的直接丢。这里最容易翻车的是只做了「不丢」没做「去重」——重复推一条比丢一条更烦,比如重复弹一条扣费提醒,用户直接投诉。
落地顺序建议先定 seq 方案,再谈推送通道。通道选型(长轮询 / WebSocket / 私有协议)影响的是延迟和成本,不影响这三点能否成立。