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

哈Ha!



今天,我们提请您注意有关微服务和分布式体系结构的少量材料。特别是,它触及了马丁·福勒(Martin Fowler)的观点,即新系统应从整体开始,即使在已开发的微服务体系结构中,建议也保留较大的整体核心。



享受阅读!



今天,每个人都在考虑微服务并编写它们-我也不例外。基于微服务的基本原理及其真实上下文,很明显,微服务是分布式系统。



什么是分布式交易?



跨越网络上的多个物理系统或计算机的事务简称为分布式事务。在微服务领域,一个事务被分成多个服务,这些服务被依次调用以完成整个事务。



下面是使用事务的单片网店系统:







图。 1:Monolith



中的交易如果在上述系统中,用户向平台发送订单请求(结帐),则平台在数据库中创建本地交易,并且该交易跨越许多数据库表来处理(处理)订单和保留(储备)来自仓库的货物。如果这些步骤中的任何一个失败,则交易可以回滚,这意味着订单本身和保留商品都被拒绝。这套原则称为ACID(原子性,一致性,隔离性,耐久性),并在数据库系统级别得到保证。



这是从微服务构建的在线商店系统的分解







图2:微服务中的交易



分解该系统之后,我们创建了微服务,OrderMicroserviceInventoryMicroservice与单独的数据库。当用户发出订单请求(结帐)时,这两个微服务都会被调用,并且每个微服务都会对其数据库进行更改。由于事务现在分布在多个系统的多个数据库中,因此被视为分布式



在微服务中提交分布式事务时会出现什么问题?



随着微服务架构的引入,数据库正在失去其ACID性质。由于许多微服务之间以及数据库之间的交易可能激增,因此必须解决以下关键问题:



如何保持交易的原子性?



原子性意味着在任何事务中,要么所有步骤都可以完成,要么不完成。如果以上示例未能完成方法中的“订单项”操作InventoryMicroservice,那么如何回滚已应用的“订单处理”中的更改OrderMicroservice



如何处理竞争性要求?



假设来自任何微服务的对象进入数据库进行长期存储,并且同时另一个请求读取相同的对象。服务应返回什么数据-旧的还是新的?在上面的示例中,当它OrderMicroservice已经完成工作并且InventoryMicroservice正在更新时,您是否需要在用户下达的订单请求数量中包括当前订单?



现代系统在设计时就考虑到了潜在的故障,Pat Helland很好地阐明了分布式事务处理中的主要问题之一。

通常,开发人员根本不会制作会涉及分布式事务的大型可扩展应用程序。



可能的解决方案



在设计和构建基于微服务的应用程序时,以上两个问题非常关键。为了解决这些问题,使用了以下两种方法:



  • 两相固定
  • 最终一致性和补偿/ SAGA


1.两阶段固定



顾名思义,这种处理事务的方法涉及两个阶段:准备阶段和提交阶段。在这种情况下,事务协调员在组织事务的生命周期中起着重要的作用。



怎么运行的



在准备阶段,所有参与工作的微服务都准备提交并通知协调器它们准备完成交易。然后,在下一步中,要么发生提交,要么事务协调器向所有微服务发出命令以回滚。



再次考虑一个在线商店系统作为示例:







图3:微服务系统中成功的两阶段提交在



上述示例(图3)中,当用户提交订单请求时,协调器TransactionCoordinator首先启动具有完整上下文信息的全局事务。首先,它将prepare命令发送到微服务OrderMicroservice以创建订单。然后将prepare命令发送到InventoryMicroservice预定物品。当两个服务都准备好进行更改时,它们阻止对象进行进一步的更改并通知它TransactionCoordinator。一旦TransactionCoordinator确认所有微服务已准备好应用其更改,它将通过请求事务提交命令这些微服务保存它们。此时,所有对象将被解锁。





图4:使用微服务时失败的两阶段提交



在故障场景中(图4)-如果任何时候单个微服务都没有时间准备,请TransactionCoordinator取消事务并开始回滚过程。在图表上,OrderMicroservice由于某种原因,我无法创建订单,但InventoryMicroservice回答说我准备创建订单。协调员TransactionCoordinator将在以下位置请求取消InventoryMicroservice,之后该服务将回滚所有所做的更改并解锁数据库对象。



好处



  • 这种方法保证了事务的原子性。当两个微服务都成功执行时,或者当微服务不进行任何更改时,事务将完成。
  • 其次,此方法使您可以将读取与写入隔离开,因为在事务协调器提交这些更改之前,对对象的更改是不可见的。
  • 这种方法是一个同步调用,其中将通知客户端成功或失败。


缺点



  • 没有什么是完美的; 与单微服务操作相比,两阶段提交的速度相当慢。他们高度依赖协调员。事务,这会在高负载期间显着降低系统速度。
  • 另一个主要缺点是数据库行锁定。锁定可能会成为性能瓶颈,并且可能会发生死锁,其中两个事务会彼此紧密锁定。


2.最终一致性和补偿/ SAGA



最终,一致性的最佳定义之一是在microservices.io上给出的:每个服务在数据更新时都会发布一个事件。其他服务订阅事件。收到事件后,服务将更新其数据



通过这种方法,分布式事务被执行为相应微服务上异步本地事务的集合。微服务通过事件总线交换信息。



怎么运行的



再次,让我们以在线商店中运行的系统为例:







图5:最终一致性/ SAGA,成功



在上面的示例(图5)中,客户要求系统处理订单。该请求Choreographer引发Create Order事件,该事件启动事务。微服务OrderMicroservice侦听此事件并创建订单-如果此操作成功,则会引发Order Created事件。协调员会Choreographer监听此事件并继续订购商品,从而引发“储备商品”事件。微服务InventoryMicroservice收听此事件并订购商品;如果此事件成功,它将引发Items Reserved事件。在此示例中,这意味着事务已结束。



微服务之间的所有基于事件的通信都是通过事件总线发生的,并且另一个系统负责其组织(编排)-这就是解决问题的方法,具有不必要的复杂性。







图6:最终一致性/ SAGA,失败结果



如果由于某种原因InventoryMicroservice未保留项目(图6),则会引发“未能保留项目”事件。协调器Choreographer侦听此事件并启动抵消交易,从而引发Delete Order事件。微服务OrderMicroservice 侦听此事件并删除以前创建的订单。



好处



这种方法的主要优点是每个微服务仅专注于自己的原子事务。如果另一个服务需要相对较长的时间才能运行,则微服务不会被阻止。这也意味着您也不需要锁定数据库。通过这种方法,可以确保在高负载下工作时系统的良好可伸缩性,因为所提出的解决方案是异步的并且基于处理事件。



缺点



这种方法的主要缺点是它不提供读取隔离。因此,在上面的示例中,客户将看到订单已创建,但是在一秒钟之后,订单将在抵消交易期间被删除。此外,随着微服务数量的增加,它们变得更加难以调试和维护。



结论



提议的方法的第一个替代方案是完全放弃分布式事务。如果要构建新的应用程序,请从Martin Fowler的MonolithFirst中描述的整体架构开始。我会引用他的。

, , . , , . —
如果由于单个事件需要一次在两个位置更新数据,则最终一致性/ SAGA方法比处理两阶段方法更适合于处理分布式事务。主要原因是分布式环境中的两阶段方法无法扩展。使用一致性最终也会引发一系列问题,例如如何自动更新数据库并触发事件。继续这种发展哲学,有必要从开发人员的角度和测试人员的角度来改变其观念。



All Articles