微服务架构中的分布式事务

本编博客主要介绍分布式事务的概念以及行业解决方案。微服务架构越来越流行了。然而,微服务中一个普遍的问题是如何跨多个微服务管理分布式事务。 这篇文章将分享一些经验,并解释问题和可能解决该问题的模式

分布式事务概念

什么是分布式?什么是事务?

什么是分布式事务

当微服务架构将单体系统分解为多个服务时,它可能会打破之前的事务。 微服务架构意味着单体系统中的本地事务现在被分配到多个服务中,这些服务将按顺序调用。

首先我们来看一个单体系统当中使用local transaction的客户订单示例:

微服务架构中的分布式事务

在上面的客户订单示例中,如果用户向一个单体系统执行“下订单Put Order”操作,则系统将创建一个在多个数据库表上工作的本地数据库事务。 如果任何步骤失败,则事务可以回滚。 这称为ACID(原子性,一致性,隔离性,耐久性),由数据库系统保证。

现在我们分解该系统为微服务,然后我们创建了CustomerMicroservice和OrderMicroservice两个微服务,它们具有单独的数据库。 拆分之后,下面是客户使用微服务的客户订单示例:

微服务架构中的分布式事务

当用户发出“PUT Order”请求时,两个微服务都将被调用, 将更改应用到他们自己的数据库中。 由于该事务现在跨多个数据库,因此被视为分布式事务。

分布式事务的问题是什么?

在单体系统中,我们有一个数据库系统来确保ACIDity。 现在,我们需要澄清以下关键问题。

在数据库系统中,原子性意味着在事务中所有步骤都已完成或没有步骤已完成。 默认情况下,基于微服务的系统没有全局事务协调器。 在上面的示例中,如果CreateOrder方法失败,我们如何回滚CustomerMicroservice应用的更改?

如果对象是由事务写入的,并且同时(在事务结束之前),则该对象被另一个请求读取,则该对象应返回旧数据还是更新数据? 在上面的示例中,一旦UpdateCustomerFund成功但仍在等待CreateOrder的响应,对当前客户资金的请求是否应返回更新后的金额?

上述问题对于基于微服务的系统很重要。 否则,无法判断交易是否成功完成。 以下两种模式可以解决此问题:

  • 2pc (two-phase commit)
  • Saga模式

 

分布式事务问题的方案

两段提交Two-phase commit (2pc) pattern

2pc广泛用于数据库系统。 在某些情况下,您可以将2pc用于微服务。 注意; 并非所有情况都适合2pc,事实上,在微服务架构中2pc被认为是不切实际的(在下面进行解释)。

那么什么是两阶段提交?

顾名思义,2pc具有两个阶段:准备阶段和提交阶段。 在准备阶段,将要求所有微服务准备一些可以原子完成的数据更改。 一旦准备好所有微服务,提交阶段将要求所有微服务进行实际更改。

通常,需要有一个全局协调器来维护事务的生命周期,并且该协调器将需要在准备和提交阶段调用微服务。

这是客户订单示例的2pc实现:

微服务架构中的分布式事务

在上面的示例中,当用户发送订单请求时,协调器将首先使用所有上下文信息创建全局事务。然后它将告诉CustomerMicroservice准备用创建的交易更新客户资金。然后,CustomerMicroservice将检查例如客户是否有足够的资金来进行交易。一旦CustomerMicroservice可以执行更改,它将锁定该对象,使其免受进一步的更改,并告诉协调器已准备好。在OrderMicroservice中创建订单时会发生相同的事情。一旦协调员确认所有微服务都准备好应用其更改,它将通过请求事务提交来要求它们应用更改。此时,所有对象将被解锁。

如果任何单个微服务在任何时候都无法准备,则协调器将中止事务并开始回滚过程。这是客户订单示例的2pc回滚图:

微服务架构中的分布式事务

在上面的示例中,CustomerMicroservice由于某种原因未能准备,但是OrderMicroservice答复说它已准备好创建订单。 协调器将请求OrderMicroservice上的事务中止,然后OrderMicroservice将回滚所做的所有更改并解锁数据库对象。

2pc的好处

2pc是一个非常强大的一致性协议。 首先,准备和提交阶段确保事务是原子的。 事务将以所有微服务成功返回或所有微服务没有任何更改的方式结束。 其次,2pc允许读写隔离。 这意味着在协调员提交更改之前,对字段的更改是不可见的。

 2pc的缺点

尽管2pc解决了该问题,但实际上并不建议将其用于许多基于微服务的系统,因为2pc是同步的(阻塞)。 该协议将需要在事务完成之前锁定将要更改的对象。 在上面的示例中,如果客户下订单,则该客户的“资金”字段将被锁定。 这样可以防止客户应用新订单。 这是有道理的,因为如果“准备好的”对象在声明它是“准备好的”之后发生了更改,则提交阶段可能无法正常工作。

情况不妙。 在数据库系统中,事务往往会很快-通常在50毫秒内。 但是,微服务在RPC调用上有很长的延迟,尤其是在与外部服务(如支付服务)集成时。 该锁可能成为系统性能的瓶颈。 同样,当每个事务请求对另一个请求的资源进行锁定时,两个事务可能会相互锁定(死锁)。

SAGA模式

Saga模式是另一种广泛用于分布式事务的模式。 它不同于2pc,后者是同步的。 Saga模式是异步的和反应性的。 在Saga模式中,分布式事务由所有相关微服务上的异步本地事务完成。 微服务通过事件总线event Bus相互通信。

这是客户订单示例的Saga模式图:

微服务架构中的分布式事务

在上面的示例中,OrderMicroservice收到下订单的请求。 它首先启动本地事务以创建订单,然后发出OrderCreated事件。 CustomerMicroservice会监听此事件,并在收到事件后更新客户资金。 如果从资金中成功扣除,则将发出CustomerFundUpdated事件,在此示例中,这表示交易结束。

如果任何微服务无法完成其本地交易,则其他微服务将运行补偿交易以回滚更改。 这是补偿交易的Saga模式图:

微服务架构中的分布式事务

在上面的示例中,UpdateCustomerFund由于某种原因失败,然后发出了CustomerFundUpdateFailed事件。 OrderMicroservice侦听事件并开始其补偿事务以还原已创建的订单。

SAGA模式的优点

Saga模式的一大优势是它支持长期交易。 因为每个微服务仅专注于其自己的本地原子事务,所以如果微服务长时间运行,则不会阻止其他微服务。 这也允许事务继续等待用户输入。 另外,由于所有本地事务都是并行发生的,因此任何对象都没有锁定。

Saga pattern的缺点

Saga模式很难调试,尤其是在涉及许多微服务时。 而且,如果系统变得复杂,事件消息可能变得难以维护。 Saga模式的另一个缺点是它没有读取隔离。 例如,客户可能看到正在创建的订单,但是在下一秒钟,由于补偿交易,订单被删除。

结论

Saga模式是解决基于微服务的体系结构中的分布式事务问题的首选方法。 但是,它也引入了一系列新问题,例如如何自动更新数据库并发出事件。 采用Saga模式需要在开发和测试方面改变观念。 对于不熟悉这种模式的团队来说,这可能是一个挑战。 有许多变体可以简化其实现。 因此,选择正确的方法来为项目实施它很重要。