5.3.1 可靠事件队列 2008 年,eBay 系统架构师 Dan Pritchett 在 ACM 发表了论文《Base: An Acid Alternative》[^1]。这篇论文中,作者基于实践总结出一种独立于 ACID 之外,通过消息队列和幂等机制来达成数据一致性的技术手段,并提出了“最终一致性”的概念。 从论文标题可以看出,最终一致性的概念与 ACID 强一致性对立。因为 ACID 在英文中有的“酸”的含义,这一事务模型的名字刻意拼凑成 BASE(BASE 在英文中有碱的含义)。有酸 vs 碱这个计算机浑然天成的梗加成,《Base: An Acid Alternative》论文广泛传播,BASE 理论和最终一致性的概念也被大家熟悉。
2008 年,eBay 系统架构师 Dan Pritchett 在 ACM 发表了论文《Base: An Acid Alternative》1。这篇论文中,作者基于实践总结出一种独立于 ACID 之外,通过消息队列和幂等机制来达成数据一致性的技术手段,并提出了“最终一致性”的概念。
从论文标题可以看出,最终一致性的概念与 ACID 强一致性对立。因为 ACID 在英文中有的“酸”的含义,这一事务模型的名字刻意拼凑成 BASE(BASE 在英文中有碱的含义)。有酸 vs 碱这个计算机浑然天成的梗加成,《Base: An Acid Alternative》论文广泛传播,BASE 理论和最终一致性的概念也被大家熟悉。
BASE 是“Basically Available”、“Soft State”和“Eventually Consistent”的缩写,其核心理念为:
BASE 理论是对 CAP 定理中 AP(可用性和分区容错性)方案的延伸,强调在分布式系统中,即使无法实现强一致性,也可以通过适当的方式使系统最终达到一致性。
Dan Pritchett 在论文中提到的“实现数据一致性的手段”,可以总结为基于可靠事件队列的事件驱动模式。该模式的关键在于确保事件的可靠投递和避免重复消费。幸运的是,当前流行的消息中间件已经普遍实现了事件持久化和至少一次投递机制。此外,幂等性的实现也有成熟的解决方案,因此这些问题已不再是难点。
接下来,笔者将通过一个具体的例子帮助你理解“可靠事件队列”的具体做法。
假设有一个电商系统,下单操作依赖于三个服务:支付服务(进行银行扣款)、库存服务(扣减商品库存)和积分服务(为用户增加积分)。下单过程中,我们优先处理最核心、风险最高的服务,按照支付扣款、仓库出库以及为用户增加积分的顺序执行。下单的整个流程如图 5-2 所示。
:::center
图 5-2 可靠事件队列模型
:::
首先,用户向商店发送了一个交易请求,如购买一件价值 ¥100 的商品。接着,支付服务创建一个本地扣款事务。如果扣款事务执行成功,系统将在消息队列中新增一条待处理消息。消息的大致结构如下:
struct Message { 事务 ID; 扣款 ¥100(状态:已完成); 仓库出库(状态:待处理); 赠送积分(状态:待处理) }
系统中有一个持续运行的服务,定期轮询消息队列,以检查是否存在待处理的消息。如果发现待处理消息,它将通知库存服务和积分服务进行相应的处理。
此时,会出现以下几种情况:
由此可见,在可靠消息队列方案中,一旦第一步扣款成功,后续操作便不再考虑失败回滚的概念,只有成功一条路可选。
这种依赖持续重试来确保可靠性的解决方案在计算机领域被广泛应用,它还有专有的名称 —— “最大努力交付(Best-Effort Delivery)”。因此,可靠事件队列也称为“最大努力一次提交(Best-Effort 1PC)”机制,也就是将最容易出错的业务通过本地事务完成后,借助不断重试的机制促使同一分布式事务中其他相关业务也顺利完成。