当前位置: 首页 > 科技观察

一篇文章搞定!10分钟详解Saga分布式事务

时间:2023-03-17 17:50:04 科技观察

转载请联系石山架构笔记公众号。开放随着微服务架构的兴起,越来越多的公司在实际场景中会遇到分布式事务的问题。尤其是在金融应用场景中,几个跨进程的应用共同完成一个任务时,更离不开分布式事务的参与。对于分布式事务,经常会提到2PC和TCC。但是面对业务流程长、TCC改造难度大的场景,就会用到Saga分布式事务。今天介绍一下Saga实现分布式事务的内容:Saga的分布式解决方案Saga处理事务一致性Saga分布式事务协调Saga的分布式解决方案随着互联网的高速发展,原来的单体应用变得困难了支持大流量和高并发需求,软件系统逐渐从原来的单体应用向分布式应用过渡。如图1所示,左侧的WebApp包含UI和服务模块。改造后会对应到微服务架构中,服务之间是相互调用的关系。图1从单体到分布式系统架构的转变分布式部署后,会有多个服务共同完成一个事务操作,而这些服务存在于不同的服务器或网络环境中,需要通过网络将服务远程协同事务调用分布式事务。例如:银行转账业务、订单紧固件库存等。在分布式事务场景下,如果对数据有很强的一致性要求,业务层会实现“两阶段提交”(2PC)方案。如果保证最终一致性,可以采用TCC(TryConfirmCancel)方式。虽然TCC保证最终一致性的模式在业界应用广泛,但是对于一些分布式事务场景,流程多,流程长,可能会调用其他公司的服务。尤其是不可控的服务(其他公司的服务),这些服务不能按照TCC开发模式,导致TCC模式的开发成本更高。具体体现在具体场景中,以核心金融业务(渠道层、产品层、集成层)为代表,其特点是:流程多、流程长、业务调用不可控。同时应该是流程长,事务边界太长,加锁时间长。使用TCC模式会影响并发性能。针对此类业务场景的分布式事务处理,提出了Saga分布式处理模式。Saga是一个“长事务解决方案”,更适合“业务流程长、业务流程多”的场景。特别地,参与交易的服务是遗留系统服务。此类服务无法提供TCC模式下的三个接口,可以采用Saga模式。其适用的业务场景包括金融机构对接系统(需要与外部系统对接)、渠道整合(长流程)、分布式架构服务等。其优点是本地交易一次性提交,无锁,高性能;参与者可以异步执行,吞吐量高;补偿服务易于实现,因为更新操作的逆向操作相对容易理解;当然它也有缺点,就是不能保证Isolation。Saga处理事务一致性1987年,普林斯顿大学的HectorGarcia-Molina和KennethSalem发表了一篇关于如何处理长期事务的PaperSagas。Saga是一个长期存在的事务,可以分解为一组可以交错的子事务的集合。这些子事务中的每一个都是维护数据库一致性的真实事务。正如这家伙的论文中提到的,每个Saga由一系列子事务Ti组成。每个Ti都有一个对应的补偿动作Ci,用来撤销Ti造成的结果。这里可以理解为每一个分布式事务的每一个执行操作或者步骤就是一个Ti,比如T1是扣库存,T2是创建订单,T3是支付业务。然后,每个Ti对应一个补偿动作Ci,如恢复库存C1、回滚订单C2、回滚付款C3。Saga事务有两种恢复策略:前向恢复(forwardrecovery),即“往前走”。对于执行失败的交易,他们会尝试重试交易。这里有一个假设,即每个子交易最终都会成功。这种方法适用于必须成功的场景。如图2所示,在上面的图例中,子事务是按照从左到右的顺序执行的。T1执行完后,T2执行,然后是T3、T4、T5。图2Saga事务执行策略事务回收的顺序也是按照:T1、T2、T3、T4、T5的方向。如果T1执行失败,则重试T1,以此类推执行失败的子事务。执行哪个事务。这就是为什么它被称为“前进”。后向恢复(backwardrecovery),当事务执行失败时,补偿所有已完成的事务,是一种“一退到底”的方法。如图2所示,在下面的图例中,子交易仍然是从左到右执行的。当事务T3执行时,事务执行失败,所以按照红线方向执行补偿事务,先执行C3再执行C2、C1,直到T0、T1的补偿事务C1、C2、C3,和T2都被执行了。即回滚整个Saga的执行结果。Saga分布式事务协调上面介绍了Saga的概念和事务恢复方法。每个事务都有多个子事务,每个子事务都有一个补偿事务,在事务回滚时使用。由于在分布式系统架构中,子事务对应的操作部署在不同的服务中,因此需要协调这些子事务才能完成一个共同的事务。实际上,在启动一个Saga事务时,协调逻辑会告诉第一个Saga参与者,也就是子事务,去执行本地事务。事务完成后,Saga会按照执行顺序调用Saga下一个参与的子事务。这个过程会一直持续到Saga事务被执行。如果子事务对应的本地事务在子事务执行过程中失败了,Saga会以相反的顺序执行补偿事务。一般来说,我们把Saga执行事务的顺序称为一个Saga的协调逻辑。这种协调逻辑有两种模式,Choreography和Orchestration如下:Choreography:参与者(子事务)之间的调用、分配、决策和排序是通过交换事件来进行的。这是一种去中心化的模式。参与者通过消息机制进行通信,通过监听器监听其他参与者发送的消息,进行后续的逻辑处理。由于没有中间协调点,相互协调是通过参与来进行的。控制(Orchestration):Saga提供了控制类,方便参与者的协调。事务执行的命令从控制类发起,按照逻辑顺序请求Saga的参与者。收到参与者的反馈后,控制类发起对其他参与者的调用。所有Saga参与者都围绕这个控制类进行沟通和协调工作。下面举例介绍这两种协调方式。假设有一个订单业务,从订单服务的创建订单操作开始,依次调用支付服务中的支付订单、库存服务中的扣减库存、发货服务。在交付操作中,如果所有参与者(服务)中的操作(子事务)都完成,则认为整个订单交易完成。Choreography,由于没有中央控制类参与参与者操作之间的协调,所以通过发送消息来协调。如图3所示:图3编排模式-事务执行成功1.在“订单服务”中执行“创建订单”操作,此时会向队列发送一条“创建订单消息”。2、“支付服务”监听队列中的订单消息,调用“支付订单”的操作,同时向队列发送“服务专用消息”。3、“库存服务”监听到“支付消息”后,会执行“扣库存”流程,并发送“扣库存消息”等待下一个消费者接受。4.“海运服务”是整个交易的最后一个子交易。收到“InventoryDeductionMessage”后,执行发货子事务。完成交易后,它会发送一条“ShippingMessage”到“OrderService”。订单服务收到消息后,完成整个交易闭环并提交。以上是交易执行成功。交易执行失败怎么办?如图4所示:图4排列方式-交易执行失败1.假设子交易在执行“delivery”时失败,会发送“ShippingFailedMessage”。2、库存服务收到“发货失败消息”后,会执行“回滚库存”操作,将原来扣减的库存补回来,并发送“扣减失败消息”。3、“支付服务”收到“扣费失败信息”后,会执行“回滚支付”进行退款操作,同时发送“支付失败信息”。订单服务收到消息后,会将订单交易标记为失败。从上面的描述中,我们可以看出编排的好处:简单:每个子事务运行时只需要发布事件消息,其他子事务监听处理即可。松耦合:参与者(服务)之间通过订阅事件进行通信,组合起来会更加灵活。当然,也有一些缺点:理解困难:没有完整的业务流程描述,需要阅读代码才能理解整个交易的执行过程。增加了开发者理解和维护代码的难度。服务存在循环依赖:由于通过消息和事件进行通信,参与者之间会存在循环依赖。也就是服务A调用服务B,服务B调用服务A的情况。这也增加了架构设计的复杂度,需要在设计初期慎重考虑。紧耦合风险:每个参与者执行的方法依赖于上一步参与者发送的消息,但需要订阅上一步参与者的所有消息才能了解参与者的真实状态,这无形中增加了两个服务耦合。控制(Orchestration),它的核心是定义一个控制类,它会告诉参与者(服务)应该执行什么操作(子事务)。Saga控制类通过命令和异步回复与参与者进行交互。如图5所示:图5ControlMode-Success1.订单服务在执行订单交易时,向Saga协调器发送请求命令。Saga协调器收到命令后,按照子事务执行的先后顺序调用服务中的方法。2、一开始执行“支付订单”操作,在“支付服务”中调用“支付订单”操作,通过虚线返回执行结果“支付完成”。3、接下来,执行“库存服务”中的“扣减库存”方法,同样将扣减完成信息通过虚线返回给“请求反馈”模块。4、接下来就是执行“delivery”命令,调用“deliveryservice”中的“delivery”方法,返回“deliverycomplete”的响应。5.最后三个子事务执行完毕后,返回订单服务,完成整个分布式事务。引入完成,交易成功后,我们来看一下异常情况。如图6所示:图6控制模式-失败1.执行“shipment”命令时,发现“shipmentfailure”,于是“shipmentservice”反馈给Sagacoordinator。2、此时协调员调用“库存服务”中的“回滚库存”操作,恢复扣除的库存。3、然后在“支付服务”中调用“回滚支付”,完成支付退款的工作。4.最后通知订单服务交易失败。需要指出的是,控制方式也是事件驱动的。与编排模式一样,发送消息通知参与者执行命令。上面两图中命令的执行和调用也是通过消息来进行的。controller设计的优点:避免循环依赖:编排模式下参与者之间存在循环调用,中控类可以避免这种情况的发生。降低复杂性:所有事务都交给控制器,控制器负责命令的执行和回复的处理。参与者只需要完成自己的任务即可,无需考虑消息的处理方式,降低了参与者接入的复杂度。易于测试:测试工作集中在集控类,其他服务可独立测试其功能。易于扩展:如果事务需要增加新的步骤,只需要修改控制类,保持事务复杂度线性,回滚更容易管理。当然,这种做法也有缺点:对控制器的依赖:将过多的逻辑集中在控制器中的风险。增加管理难度:这种模式除了需要管理各种业务服务外,还需要额外的管控服务,无形中增加了管理的难度和复杂度。此外,存在单点风险。一旦控制器出现故障,整个业务就会瘫痪。总结这里是Saga的总结。首先,Saga是一个分布式长寿命事务的解决方案。对于长期的、多笔的、复杂的交易,尤其是多家公司开发的服务不可控,可以采用Saga模式进行分发。事务处理。Saga在处理事务一致性时采用前向恢复和后向恢复策略。前者通过不断重试来保证事务的完成,而后者则通过子事务补偿事务逐条回滚,使事务标记失效。在分布式协调方面,Saga采用编排和控制两种模式。前者允许参与者(服务)通过消息进行通信,并根据事件触发交易的执行过程,是一种去中心化的模型。后者通过中控类处理事务的执行和回滚步骤,统一调用服务并接收服务的反馈。