微服务分布式事务
当微服务架构将单体系统分解为一个个单一微服务时,它可能破坏了事务。这意味着单体系统中的本地事务将分布到按顺序调用的多个服务中。
以下是使用本地事务的单体系统的客户订单示例:
在上面的客户订单示例中,如果用户将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模式需要改变开发和测试的思维方式。对于不熟悉这种模式的团队来说,这可能是一个挑战。有许多变体可以简化其实现。因此,选择适当的方式为项目实施它是很重要的。
相关推荐
LeeLuffy 2020-10-16
zjuwangleicn 2020-09-04
loviezhang 2020-08-08
粗茶淡饭 2020-06-25
花落花开春去秋来 2020-06-20
wenjieyatou 2020-06-09
middleware0 2020-06-09
韩学敏 2020-06-08
CharlesYooSky 2020-06-06
isHooky 2020-05-30
打不死的小强 2020-07-03
夙梦流尘 2020-06-28
loviezhang 2020-06-16
亦碎流年 2020-04-17
wqbala 2020-06-04
zhangll00 2020-05-11
wqbala 2020-05-05