0 前言
如今,软件服务可以由多个微服务组成,共同维护系统的 "状态"。在分布式软件设计中,最常见的挑战之一就是保持一致性。当构成整体服务的不同服务与系统状态不一致时,不一致的系统会引发各种问题。
1 示例
试想一下,在一个电子商务网站中,当客户下订单时,我们必须更新订单表以记录下的订单,更新奖励表以记录获得的奖励积分。
在上述单体架构中,这很简单。启动一个事务,更新必要的表,然后提交事务。如果出现任何问题,所有更改都会回滚。但是,如果由不同的服务处理订单历史记录和奖励,会发生什么情况呢?
这就是所谓的分布式事务。为了使我们的系统保持一致,必须由多个服务处理一个事件,如果其中一个服务出现故障,所有其他服务都必须回滚其更改。
1.1 并不快乐的道路
但是,如果在向订单表中写入内容后发生错误怎么办?服务器可能会崩溃,请求可能会超时,或者奖励服务甚至可能无法处理事件。这会导致系统状态不一致。用户虽然下了订单,但却得不到奖励积分。
事件驱动架构可确保奖励服务在事件发布到事件队列后至少处理一次事件。在订单表中插入订单后,订单服务会向奖励服务发送一个事件,奖励服务最终会处理该事件并更新奖励表中所需的奖励积分。
我们最终实现了一致性。但还不够。我们还没有解决服务器在将事件写入事件队列之前崩溃的问题。如果我们未能将事件发送到事件队列中,该怎么办?
注意:在使用消息代理时,总是有可能出现潜在的重复消息。无论采用哪种模式,都必须将服务设计成可幂等的,这一点很重要。
让我们来看看我们可以用来保证奖励服务接收事件的一些模式。
2 模式 1:发件箱模式
发件箱模式将事件存储在与订单表相同的数据库(在本例中)中的发件箱表中。对数据库的写入可以在一个事务中完成,因此我们可以在一个事务中插入订单和发件箱事件。如果其中一个操作失败,另一个也会回滚。
为了发出事件,奖励服务可以使用更改数据捕获(CDC)来跟踪表中的更改并进行处理。CDC 指的是跟踪对数据集中的数据所做的更改。例如,亚马逊 DynamoDB 提供 DynamoDB 流来捕获表中的更改。
记得管理发件箱表
这一点经常被遗忘。已处理的事件应从发件箱表中删除。这样可以防止表越来越大。
3 模式 2:原始事件处理
我们讨论过在发件箱表中插入事件,并在奖励服务中处理这些数据更改事件。如果只有在订单历史记录表发生变化时才需要向奖励服务发送事件,则可以在订单历史记录表上使用 "更改数据捕获 "和事件筛选器,而不必创建单独的发件箱表。每当有新订单添加到订单历史记录表时,就可以捕获这一数据变化,并将其作为一个事件发送到奖励服务。

4 模式 3:读懂自己模式
在上述两个示例中,我们都希望有一个能在单个事务下发出事件的单点。为此,我们可以通过订单历史记录服务发布该事件,并由订单历史记录服务和奖励服务共同消费该事件。

在这里,如果步骤(2)成功,则可以保证订单和奖励服务最终都会处理该事件,我们系统的状态也将保持一致。
本文已收录在Github,关注我,紧跟本系列专栏文章,咱们下篇再续!
作者简介:魔都架构师,多家大厂后端一线研发经验,在分布式系统设计、数据平台架构和AI应用开发等领域都有丰富实践经验。
各大技术社区头部专家博主。具有丰富的引领团队经验,深厚业务架构和解决方案的积累。
负责:
- 中央/分销预订系统性能优化
- 活动&券等营销中台建设
- 交易平台及数据中台等架构和开发设计
- 车联网核心平台-物联网连接平台、大数据平台架构设计及优化
- LLM Agent应用开发
- 区块链应用开发
- 大数据开发挖掘经验
- 推荐系统项目
目前主攻市级软件项目设计、构建服务全社会的应用系统。
参考:
















