微服务分布式事务

18-10-04 banq
                   

当微服务架构将单体系统分解为一个个单一微服务时,它可能破坏了事务。这意味着单体系统中的本地事务将分布到按顺序调用的多个服务中。

以下是使用本地事务的单体系统的客户订单示例:



在上面的客户订单示例中,如果用户将Put Order 操作发送到单体系统,则单体系统将创建一个作用于多个数据库表上的本地数据库事务。如果其中任何一个步骤失败,则整个事务回滚。这种ACID(原子性,一致性,隔离性,耐久性)是由数据库系统保证。

当我们分解这个系统中,我们创建了两个 CustomerMicroservice和 OrderMicroservice微服务,各自具有单独的数据库。这是一个带有微服务的客户订单示例:



当 Put Order请求来自用户时,将调用两个微服务,并将修改操作应用到他们自己的数据库中。由于事务现在跨多个数据库,因此它现在被视为 分布式事务。

在单体系统中,我们有一个数据库系统来确保ACIDity。那么在分布式如何保证?

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

我们是否针对并发请求隔离用户操作了?( ACID的隔离性)
如果一个对象是由两个事务同时操作,如何保证它们之间的隔离?在上面的示例中,一旦UpdateCustomerFund成功但仍在等待响应 CreateOrder,那么当前客户基金的请求是否会返回更新后的金额?

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

2pc(两阶段提交)

2pc广泛用于数据库系统。在某些情况下,您可以使用2pc应用到微服务。小心点; 并非所有情况都适合2pc,事实上,2pc在微服务架构中被认为是不切实际的。

那么什么是两阶段提交?

正如其名称提示,2pc有两个阶段:准备阶段和提交阶段。在准备阶段,将要求所有微服务准备一些可以原子方式完成的数据更改。一旦准备好所有微服务,提交阶段将要求所有微服务进行实际更改。

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

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



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

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



在上面的例子中,由于CustomerMicroservice某种原因未能做好准备,但OrderMicroservice已经回复说它准备创建订单。在Coordinator将请求中止对OrderMicroservice的事务,让OrderMicroservice回滚所作的任何更改和解锁的数据库对象。

使用2pc的好处
2pc是一种非常强大的一致性协议。首先,准备和提交阶段保证事务是原子的。事务将以所有微服务成功返回或所有微服务都没有改变而结束。其次,2pc允许读写隔离。这意味着在协调器提交更改之前,数据库字段上的更改是相互不可见的。

使用2pc的缺点
虽然2pc已经解决了这个问题,但是对于许多基于微服务的系统并不是真的有用,因为2pc是同步的(阻塞)。协议将需要锁定所有在事务完成之前更改的对象。在上面的示例中,如果客户下订单,则将为客户锁定“基金”字段。这可以防止客户申请新订单。这是有道理的,因为如果“准备好的”对象在声称它已“准备好”之后发生了变化,那么提交阶段可能无效。

这不是很好。在数据库系统中,事务通常在50毫秒内快速正常完成。但是,微服务在RPC调用方面存在长时间延迟,尤其是在与外部服务(如支付服务)集成时。锁可能成为系统性能瓶颈。此外,当每个事务请求锁定另一个需要的资源时,可能有两个事务相互锁定(死锁)。

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

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



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

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



在上面的示例中,由于UpdateCustomerFund某种原因失败,然后它发出了一个CustomerFundUpdateFailed事件。在OrderMicroservice该事件监听并启动其补偿交易恢复所创建的订单。

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

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

添加流程管理器
为了解决Saga模式的复杂性问题,将流程管理器添加为协调器是很正常的。流程管理器负责监听事件和触发端点。

结论
Saga模式是解决基于微服务的体系结构的分布式事务问题的优选方式。但是,它还引入了一组新问题,例如如何以原子方式更新数据库并发出事件。采用Saga模式需要改变开发和测试的思维方式。对于不熟悉这种模式的团队来说,这可能是一个挑战。有许多变体可以简化其实现。因此,选择适当的方式为项目实施它是很重要的。


原文
[该贴被banq于2018-10-04 18:42修改过]

                   

1