在 WhatsApp 上发送一条消息,虽然对用户来说似乎是即时的,但实际上涉及一系列复杂的步骤和跨各种后端数据库系统和服务的分布式事务。由于 WhatsApp 的规模庞大、全球分布,并且优先考虑可用性和性能而非强大的即时一致性,这些事务通常是最终一致性事务,而不是跨不同系统的传统 ACID(原子性、一致性、隔离性、持久性)事务。
WhatsApp 消息的旅程:关键数据库交互
让我们分解一下单个消息的典型流程和所涉及的数据库交互,记住 WhatsApp 使用多主/分散式 NoSQL 数据库(如 Cassandra/ScyllaDB)作为其核心消息数据。
1.发送方客户端(发送方客户端)
消息创建与加密:用户在客户端输入消息并点击发送。消息首先在客户端被端到端加密。
本地持久化:消息在发送前会立即在发送方设备的本地数据库(例如 SQLite)中持久化。这样可以确保消息不会导致应用程序崩溃而丢失第一步。
显示“已发送”状态:客户端立即将消息状态更新为**“单勾”**(✔),表示消息已从设备发送。
2. WhatsApp前端服务器(WhatsApp Frontend Servers)
接收消息请求:加密后的消息从发送方客户端发送到WhatsApp的前端服务器(通常是离开发送方最近的数据中心)。
验证与初步处理:服务器验证发送方身份、权限等。
写入消息队列(Message Queue Transaction):
事务类型:这通常是一个高吞吐量的、持久化的写入操作,目标是消息队列(如 Kafka)。
数据库交互:消息内容(加密形式)和元数据(发送方ID、接收方ID、计时器、消息ID等)被写入消息队列中。
目的:消息队列作为核心瀑布,确保消息不会丢失,并解耦前置服务与队列处理服务。写入消息队列成功后,前置服务器通常会向发送方客户端发送一个确认回执(ACK)。
服务器侧存储(待投递):此时,服务 哥斯达黎加 whatsapp 数据库 器端数据库(如Cassandra)可能会记录消息的元数据和短暂存储加密消息体,用于后续投递。
3.消息投递服务(Message Delivery Service)
从队列消费消息:队列的消息投递服务(作为消息队列的消费者)从队列中拉取消息。
路由与查找接收方:服务查找接收方当前在线状态、设备信息和所在数据中心。
更新消息状态(数据库事务 - 交付尝试):
事务类型:这是一个最终的一致性写入。
数据库交互:在消息投递服务尝试向接收方设备主动消息时,它可能会在核心消息数据库(如Cassandra)中更新该消息的投递状态(例如,从“队列中”变为“尝试投递”)。此更新是针对消息的元数据进行的。
4. 接收方客户端(Recipient Client)
接收消息:接收方设备收到消息(可以通过长连接自适应)。
本地持久化:消息在接收方设备的本地数据库中解密并持久化。
发送“已送达”回执(数据库交易-送达回执):
事务类型:这是一个由接收方客户端发起的异步写入事务。
数据库交互:接收方客户端成功接收消息后,会向 WhatsApp 服务器发送一个“已发送达”确认回执。服务器收到此回执后,会在核心消息数据库中将消息的状态更新为**“双勾”**(✔✔)。
传播给传播方:数据库更新成功后,国家变更事件会异步传播给传播方客户端,更新其界面显示。
发送“已读”回执(数据库事务-已读回执):
事务类型:这是一个由接收方客户端发起的异步写入事务。
数据库交互:当接收方在聊天界面查看消息后,客户端会向服务器发送一个“已读”回执。服务器收到此回执后,在数据库中将消息状态更新为**“蓝勾”**
传播给发送方:同样,此状态变更事件会异步传播给发送方客户端,更新其界面显示。
5.服务器端清理(Server-side Clean-up)
消息过期与删除:一旦消息成功投递到所有目标设备,并且相关的状态(如已送达、已读)已确认,服务器上的消息内容通常会在短暂的缓冲期后被标记为删除或过期。这是为了实现 WhatsApp不永久存储已投递消息内容的隐私原则。这个过程通常是数据库的**清理后台任务(如压缩)**的一部分。
事务的最终一致性
在 WhatsApp 中,上述数据库交互通常不是同类的ACID 事务,因为:
性能和可用性优先:强制在所有步骤上实现强一致性会牺牲性能和可用性,这对于实时通讯应用来说是不重要的。
最终一致性:大多数状态更新和数据流都依赖于最终一致性模型。这意味着在某个时间点,发送方和接收方看到的消息状态可能会有所不同,但随着事件的异步传播和处理,状态最终会趋于一致。
幂等操作:所有的数据库读取操作和消息处理逻辑都必须是幂等的,即重复执行多次也不会产生副作用。这对于处理网络重试和异步消息传递至关重要。
通过这种事件驱动、异步处理和最终一致性的设计,WhatsApp能够处理海量的消息量,同时保证高可用性和可靠性。