【.com快言】在微服务架构中,单个服务内的事务通常使用ACID事务来提供数据一致性。简介本文描述了一个架构和概念框架,用于管理微服务系统中的分布式和长时间运行的事务。作者发表这篇文章是为了与开发社区分享他们的经验,表达对事件驱动架构的热情,并激发对分布式系统进行复杂事件处理的讨论兴趣。概述最真实意义上的微服务是分布式系统。一个事务被分发到多个服务中,依次或并行调用这些服务来完成整个事务。在微服务架构中,单个服务中的事务使用ACID事务来提供数据一致性。然而,挑战在于处理跨多个服务的交易,在某些情况下需要很长时间才能完成。在这种情况下,应用程序必须使用复杂的机制来管理事务。ACID是指数据库管理系统(DBMS)在写入或更新数据的过程中,为保证事务的正确可靠而必须具备的四个特性:原子性、一致性、隔离性和持久性。想象一下使用微服务架构预订航空公司航班座位的简单场景。在这个场景中,一个微服务锁定预订的座位,另一个微服务接受支付,另一个微服务支付后解锁并分配座位,每个微服务执行一个本地事务。旅客必须完成所有三个步骤才能成功完成航班预订流程。如果任何一步失败,则必须回滚所有先前完成的步骤。由于整个事务的边界跨越多个服务和数据库,因此被认为是分布式事务。考虑通过微服务方法实施另一个订单履行场景。工作流交易从订单服务开始,创建订单,然后使用另一个服务进行支付,接下来为交易创建发票,然后发货,最后交付订单并完成工作流,循环遍历每个本地交易.这里的订单处理本质上是分布式的,可能需要几天到几周才能完成工作流程。这样的事务可以称为长时间运行的事务,因为使用传统的ACID事务语义无法一次执行所有步骤。挑战随着微服务架构的出现,分布式事务管理存在两个关键问题:原子性:原子性意味着事务中的所有步骤都必须成功,或者如果一个步骤失败,则应回滚之前的所有步骤。但在微服务架构中,一个事务可以由多个由不同微服务处理的本地事务组成。那么,如果其中一个本地事务失败了,如何回滚之前成功完成的事务呢?隔离:事务隔离指定事务中的语句可以看到多少数据,尤其是当多个服务调用同时访问同一个数据源时。如果来自任何一个微服务的对象持久化在数据库中,并且另一个请求同时读取同一个对象,服务应该提交旧数据还是新数据?为解决这些问题,提供高效的事务管理能力,可以采用两种方式:一种是两阶段提交(2PC);另一个是佐贺。(1)两阶段提交(2PC)跨多个服务保持数??据一致性的传统方法是使用分布式事务,其事实上的标准是两阶段提交(2PC)。两阶段提交(2PC)确保事务中的所有参与者都提交或回滚。它分两个阶段工作;阶段1称为准备阶段,控制节点询问所有参与节点是否准备好提交;阶段2称为提交阶段,如果所有节点都回答是,控制节点要求他们提交,否则回滚。尽管两阶段提交(2PC)可以帮助在分布式系统中提供事务管理,但它也可能成为单点故障,因为事务的责任落在协调器上,而此类协调器的典型实现本质上是同步的,这导致在减少未来的吞吐量。因此,两阶段提交(2PC)也有以下缺点:MongoDB、Cassandra等现代NoSQL数据库不提供支持。ApacheKafka等现代消息代理不提供支持。同步IPC降低了可用性。所有参与者都必须在场。(2)Saga为了解决微服务架构中维护数据一致性这一更复杂的问题,应用程序必须基于松耦合异步服务的概念使用不同的机制。这就是Saga发挥重要作用的地方。Saga是一种架构模式,它提供了一种优雅的方式来实现跨多个服务的事务,本质上是异步和反应式的。因此,Saga可以定义为一个事件驱动的本地事务序列,其中每个本地事务更新数据库并发出命令或事件以触发Saga中的下一个本地事务。如果本地事务因为违反业务规则而失败,Saga将执行一系列补偿事务,这些事务将撤消先前本地事务所做的更改。Saga实现确保所有事务都被执行或所有更改都被撤消,从而提供原子性保证。将Saga设计为状态机模型将为处理隔离提供一种对策。Saga模式如何提供帮助借助微服务架构,单个业务流程将多个微服务聚合在一起以提供整体解决方案。使用微服务架构实现ACID(原子性、一致性、隔离性、持久性)事务非常困难,在某些情况下甚至是不可能的。比如上面提到的航空公司订座场景,具有订座功能的微服务无法实现支付数据库的锁定,因为它在大多数情况下可能是一个外部服务。但是仍然需要某种形式的事务管理,称为BASE事务:基本可用、软状态和最终一致。必须采取补偿措施来恢复作为交易一部分发生的任何事情。下面是一个Saga如何为航班预定座位的场景图:补偿交易。假设Saga的第(n+1)笔交易失败了,那么前面n笔交易的影响肯定是undone了。从概念上讲,每个步骤Ti都有一个相应的补偿交易Ci,可以抵消Ti的影响。为了消除前n个步骤的影响,Saga必须以相反的顺序执行每个Ci。如图所示,步骤顺序为T1...Tn,Cn...C1。在此示例中,Tn+1步骤失败,这需要撤消T1。..Tn步。Saga以正向交易的相反顺序执行补偿交易:Cn...C1。Cis测序的机制与Tis测序没有任何不同。Ci的完成必须触发Ci-1的执行。PivotTransaction和RetryableTransaction下表显示了Saga在航班座位预订中每个步骤的补偿交易。这三个步骤被称为补偿交易,因为它们之后的步骤可能会失败。这里需要注意的是,并非所有步骤都需要补偿事务。Saga模式中还有另外两种事务类型;一个是Pivot事务,就像Saga中的成功/失败点。如果Pivot事务提交,Saga将运行完成。另一种是Retryable事务,跟在Pivot事务之后,保证事务成功。Saga保证Saga保证两种结果之一:Saga中的所有请求要么成功完成,要么执行请求的子集及其补偿请求。请求和补偿请求都需要遵循一定的原则:单个事务可以中止并且必须是幂等的。补偿事务必须是幂等的、可交换的,并且不能中止(必须无限期重试或在必要时通过人为干预解决)。Saga协调策略Saga执行协调器(SEC)是实现成功Saga流程的核心组件。Saga协调可以在以下领域实施:Choreography-在Saga参与者之间分配决策和排序。换句话说,参与者在没有集中控制点的情况下交换事件,每个本地事务都会发布一个域事件,触发其他服务中的本地事务。虽然Saga编排是简单可靠的基于事件的通信,但它只能处理简单的用例,并且有一些局限性使其不适合管理分布式事务。基于编排的Saga难以理解,经常产生循环依赖,Saga参与者之间存在紧耦合的风险。Orchestration-将Saga的协调逻辑集中在SagaCoordinator类中。Saga协调器向Saga参与者发送命令并根据事件的结果采取行动。协调器执行Saga请求,存储和解释每个任务的状态,并通过补偿事务处理故障恢复。基于Coordinator的Saga更适合复杂的事件处理,使其成为管理分布式事务的理想选择。SagaOrchestrator(SagaOrchestrator)正如Saga“编排器”模式所暗示的那样,有一个单独的编排器组件负责管理整个流程工作流。使用编译时,您可以定义一个协调器类,其唯一职责是告诉Saga参与者要做什么。Saga协调器使用命令/异步回复式交互与参与者进行通信。为了执行saga步骤,它向actor发送一条命令消息,告诉它该做什么。Saga参与者执行操作后,它会向协调器发送回复消息。协调器然后处理此消息并确定要执行的下一个Saga步骤。上图显示了使用Saga的基于协调器的航班座位预订过程。Saga由SagaCoordinator组件编排,它使用异步请求/响应调用Saga参与者。Saga协调器跟踪流程,通过命令组件向Saga参与者发送命令操作,例如SeatBlockingService和PaymentService,并通过事件处理程序从其回复通道中读取回复消息,然后确定下一步,使用Saga协调器预订航班座位,如下:(1)FrontEndUI向Saga协调器发送座位预订请求。(2)Saga协调器启动新的工作流,向SeatBlockingService发送SeatBlockingCommand。(3)SeatBlockingService处理命令并回复SeatBlockedEvent。(4)Saga协调器触发工作流中的下一个动作,并向支付服务发送支付请求命令。(5)支付服务(PaymentService)回复支付成功事件(PaymentSuccessEvent)。(6)Saga协调器随后向座位分配服务(SeatAllocationService)发送座位分配命令(SeatAllocationCommand)。(7)座位分配服务(SeatAllocationService)回复座位分配事件(SeatAllocatedEvent)。(8)Saga协调器结束事务,完成工作流。但是,如果锁座服务、支付服务或选座服务中的任何一个步骤失败,Saga航班预订场景可能会失败。为了有效地管理工作流和处理故障,建议将Saga建模为状态机,因为它描述了所有可能的场景并让协调器确定需要做什么。作为状态机的Saga将Saga协调器建模为状态机不仅是管理分布式事务的有效方式,也是支持长时间运行事务的有效方式。状态机由一组状态和一组由事件触发的状态之间的转换组成。每个转换都可以有一个动作,对于saga来说是对sagaactor的调用。状态之间的转换由Saga参与者执行的本地交易完成触发。本地事务的当前状态和特定结果决定了状态转换和要执行的操作。因此,使用状态机模型可以更容易地设计、实现和测试Sagas。上图突出显示了Saga航班预订的状态机模型。状态机由许多状态和转换组成,包括以下内容:OrderOpen-初始状态。saga在工作流开始时设置此状态。BlockingSeat-当处于这种状态时,Saga正在等待SeatBlockingService以阻止座位预订。授权支付-Saga正在等待支付服务对支付授权命令的响应。分配座位——支付成功后,等待分配座位服务(SeatAllocationService)分配座位。反向付款-如果座位分配失败,Saga将发送付款退款请求。UnblockSeat-如果支付授权失败,Saga将发送一个失败事件来解锁席位。OrderCompleted-指示Saga成功完成的最终状态。OrderRejected-表示被其中一位参与者拒绝的订单的最终状态。最后,Saga工作流可以重新设计为Saga状态机。Saga协调器链接到一个状态机,该状态机负责通过状态管理器API管理事务状态。除此之外,它还负责将事务状态存储在持久性数据存储设备中,以确保在系统出现故障时能够恢复。因此,Saga状态机有责任完成所有事务,或者将系统带入已知状态,以便它可以确定下一个动作状态或补偿活动可能执行的顺序,事务是否发生分布或长期存在。好处和潜在用例更简单的依赖关系-SagaCoordinator调用SagaActors,但Actors不调用Coordinator。所以协调器依赖于参与者而不是相反,所以不存在循环依赖。减少耦合——每个服务都实现一个由协调器调用的API,因此它不需要了解saga参与者发布的事件。关注点分离——Saga协调逻辑在Saga协调器中本地化。域对象更简单,并且不知道它们参与的Saga。数据一致性-在没有紧密耦合的情况下跨多个微服务保持数??据一致性。开发人员体验——该设计允许开发人员仅关注Saga参与者的业务逻辑,并简化了SagaCoordinator上有状态工作流的实现。可以实施此类实施的几个潜在用例:(1)订单管理系统电子商务食品配送航空公司预订酒店/出租车预订(2)结算交易。指南和建议如果一个组织正在设计和构建一个编排器驱动的Saga以支持分布式和长时间运行的事务,建议遵循以下指南:协调器应该只负责管理事务和状态,这里不应添加任何业务逻辑。并且应该在每个服务参与者中定义业务逻辑。所有进出协调器的事件和命令都应该携带事务数据,而不是参考数据。使用异步样式消息在服务之间进行通信。如果使用Kafka等消息代理,请实施幂等性和状态检查以提高弹性。适用于在CQRS和EventSourcing架构中设计命令端(编写模型)。原标题:ModelingSagaasaStateMachine,作者:RohitSingh
