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