SWE · System Design

Meta Messenger:消息确认、多设备与离线投递

从产品语义设计聊天,先区分发送、送达与已读,再确定消息存储和重连同步。

题目重点:从产品语义设计聊天,先区分发送、送达与已读,再确定消息存储和重连同步。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 训练先实现一对一与有限群聊、文本和媒体引用;用户多设备、短期离线。端到端加密、群规模、保留期和删除政策必须确认,不能假设所有功能都有。
  • 本题讨论服务端权威会话序号;客户端时间不作为会话全序,送达至少一设备与送达全部设备是不同产品定义。

解题路线

  1. 接口send(conversationId,clientMessageId,body)、sync(afterSeq)、ackDelivered/Read(upToSeq)。membership权限在写与读都检查,消息包含sender/device、serverSeq、createdAt,媒体另走上传服务。
  2. 服务器持久写消息和outbox后返回accepted;推送成功不等于用户设备已持久接收,更不等于已读。状态在UI分别展示,群聊多人的已读聚合明确可见范围。
  3. 按conversation路由串行分配seq,以clientMessageId去重网络重试。持久消息存储是事实,在线WebSocket/push是通知;断线重连从cursor拉取缺消息,不能仅依赖内存在线队列。
  4. 每设备cursor独立,用户级readCursor按产品规则单调max合并。多设备既不能互相覆盖降低已读进度,也不能把某设备未送达误报成全设备已收到。
  5. 群聊写一次消息,在线fanout只向连接推送引用/必要内容,离线靠同步。大群fanout需分片与预算,发送者退出、被移出成员、删除消息和附件权限有独立版本检查。
  6. 如果要求E2EE,服务端仅存密文,搜索、内容审核、密钥恢复与多设备加入会改变设计;需要明确客户密钥和元数据泄漏范围,不能给服务端明文索引后声称E2EE。

手动走一遍

客户端发m7超时重试,服务器返回同一seq42而非生成43。另一设备cursor40重连拿41/42;设备A上报read42,迟到read41不降低用户进度。socket写成功但设备尚未ACK,只能显示服务器已接收。

规模与取舍

存储约每日消息数×平均内容×保留天数×复制因子,媒体流量另算。单会话序号是局部串行瓶颈,不需要跨全球聊天同一全序;fanout按接收成员/在线设备数扩大,离线同步需分页和背压。

易错点与追问

容易失分的边界

  • 先堆技术组件而没有定义“送达”,会让数据字段无法支持产品状态。
  • 推送服务不是消息唯一持久副本。
  • 多设备已读无版本/单调规则,会被迟到旧消息覆盖。

继续深挖

  • 用户撤回消息与接收者离线时,如何通过tombstone同步?
  • 群成员历史可见性是从加入后还是可读全部?membership时间区间影响同步授权。
✓已复制微信号!