发送群聊消息,在 WhatsApp 这样大规模的分布式系统中,通常不被视为一个单一的、跨所有群成员的数据库 ACID 事务。相反,它是一个高度可靠、最终一致性的分布式“扇出”(Fan-out)操作,旨在确保所有当前群成员都能收到消息。
群聊消息发送的“事务性”与保证机制
WhatsApp 的目标是:
消息能原子性地被服务器接受并启动对所有当前群成员的投递流程。
尽最大努力保证所有群成员最终都能收到消息,即使在网络中断或成员离线的情况下。
1. 群聊消息的“原子性”理解
这里的“原子性”更多指的是:
消息一旦被发送者成功提交到 WhatsApp 服务器,服务器就能原子性地识别其为群聊消息。
服务器能原子性地查找所有当前群成员,并为每个成员原子性地创建一条待投递的任务(或将其放入相应的投递队列)。
而不是等待所有群成员都收到消息后才算成功(这在分布式环境中几乎不可能)。
2. 保证所有群成员都能收到的机制
群聊消息的投递是一个复杂的扇出过程,它将一条群消息分解为针对每个成员的独立投递任务,并为每个任务提供高可靠性:
a. 发送方提交一条消息 (Single Message Submission):
发送者客户端只向 WhatsApp 服务器发送一次加密的群聊消息(包含群 ID、消息 ID 和消息内容)。
b. 服务器处理与群成员查找 (Server Processing & Group Membership Lookup):
WhatsApp 服务器接收到群聊消息后,会查询其群组元数据服务/数据库(例如,存储群成员列表的分布式数据库),获取该群当前所有活跃成员的列表。这一步必须高度可用且准确。
c. 扇出与队列化 (Fan-out & Queuing):
这是关键一步。对于群组中的每一个当前成员,服务器会:
生成一个独立的、针对该成员的投递任务。
将这个任务(包含加密消息内容和该成员 塞浦路斯 whatsapp 数据库 的投递信息)推送到相应的持久化消息队列(如 Kafka)。通常,每个接收者可能对应一个特定的消息队列分区或主题,以便高效路由。
保证: 消息队列的持久化和高可靠性确保了即使服务器在扇出过程中崩溃,已推送到队列的任务也不会丢失,未推送到队列的则会通过重试机制再次尝试。
d. 独立投递尝试与确认 (Independent Delivery Attempts & Acknowledgements):
从消息队列中消费出针对每个成员的独立投递任务后,后续流程类似于一对一消息的投递:
消息被推送给接收方设备。
接收方设备收到并解密消息,并将其持久化到本地。
接收方客户端向服务器发送一个**“已送达”回执**。
服务器收到“已送达”回执后,在分布式数据库中更新该群聊消息针对该成员的投递状态。
当所有活跃成员都已送达时,发送方的客户端界面会显示“双勾”
e. 状态跟踪与重试 (State Tracking & Retries):
WhatsApp 后端会持续跟踪每条群聊消息对于每个群成员的投递状态(例如,已发送但未送达,已送达但未读)。
如果某个成员离线或网络不佳,针对该成员的投递任务会在消息队列中保留,并在该成员重新上线时进行重试投递。客户端也具有强大的重试机制。
f. 幂等性 (Idempotency):
所有投递操作和状态更新都设计为幂等的。即使由于网络问题或重试导致同一消息或状态更新被重复处理,也不会产生重复的消息或不一致的状态。每条消息(包括群聊消息)都有唯一的 ID。
g. 数据库持久化与最终一致性 (Database Persistence & Eventual Consistency):
群聊消息的内容(加密形式)和针对每个成员的投递状态,都会被持久化在分布式数据库中。
数据库的多副本和跨数据中心复制确保了即使部分节点故障,数据也不会丢失。
通过读修复(Read Repair)、反熵(Anti-Entropy)等机制,数据最终在所有副本间保持一致。
h. 群成员变更处理:
新成员加入: 新成员加入后,只会收到其加入后的新消息。历史消息通常不会自动同步。
成员退出: 成员退出后,不会再收到该群的后续消息。
这种处理方式简化了历史同步的复杂性,并避免了对已离开成员的无谓投递。
结论: 群聊消息的发送并非一个传统的原子事务,而是一个由服务器驱动的、针对每个群成员的独立且高度可靠的投递过程。通过消息队列的持久化、细粒度的状态跟踪、强大的重试机制、操作的幂等性以及底层数据库的分布式高可用特性,WhatsApp 能够保证群聊消息被扇出给所有当前群成员,并最终成功投递。
群聊消息的发送是否是一个事务?如何保证所有群成员都能收到?
-
- Posts: 271
- Joined: Thu Dec 26, 2024 5:46 am