题目重点:从产品语义设计聊天,先区分发送、送达与已读,再确定消息存储和重连同步。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练先实现一对一与有限群聊、文本和媒体引用;用户多设备、短期离线。端到端加密、群规模、保留期和删除政策必须确认,不能假设所有功能都有。
- 本题讨论服务端权威会话序号;客户端时间不作为会话全序,送达至少一设备与送达全部设备是不同产品定义。
解题路线
- 接口send(conversationId,clientMessageId,body)、sync(afterSeq)、ackDelivered/Read(upToSeq)。membership权限在写与读都检查,消息包含sender/device、serverSeq、createdAt,媒体另走上传服务。
- 服务器持久写消息和outbox后返回accepted;推送成功不等于用户设备已持久接收,更不等于已读。状态在UI分别展示,群聊多人的已读聚合明确可见范围。
- 按conversation路由串行分配seq,以clientMessageId去重网络重试。持久消息存储是事实,在线WebSocket/push是通知;断线重连从cursor拉取缺消息,不能仅依赖内存在线队列。
- 每设备cursor独立,用户级readCursor按产品规则单调max合并。多设备既不能互相覆盖降低已读进度,也不能把某设备未送达误报成全设备已收到。
- 群聊写一次消息,在线fanout只向连接推送引用/必要内容,离线靠同步。大群fanout需分片与预算,发送者退出、被移出成员、删除消息和附件权限有独立版本检查。
- 如果要求E2EE,服务端仅存密文,搜索、内容审核、密钥恢复与多设备加入会改变设计;需要明确客户密钥和元数据泄漏范围,不能给服务端明文索引后声称E2EE。
手动走一遍
客户端发m7超时重试,服务器返回同一seq42而非生成43。另一设备cursor40重连拿41/42;设备A上报read42,迟到read41不降低用户进度。socket写成功但设备尚未ACK,只能显示服务器已接收。
规模与取舍
存储约每日消息数×平均内容×保留天数×复制因子,媒体流量另算。单会话序号是局部串行瓶颈,不需要跨全球聊天同一全序;fanout按接收成员/在线设备数扩大,离线同步需分页和背压。
易错点与追问
容易失分的边界
- 先堆技术组件而没有定义“送达”,会让数据字段无法支持产品状态。
- 推送服务不是消息唯一持久副本。
- 多设备已读无版本/单调规则,会被迟到旧消息覆盖。
继续深挖
- 用户撤回消息与接收者离线时,如何通过tombstone同步?
- 群成员历史可见性是从加入后还是可读全部?membership时间区间影响同步授权。
