现如今,基本上所有公司都由单体架构转向了微服务等分布式架构,使用分布式系统,分布式事务的问题肯定不可避免。通常比较常见的分布式事务的实现主要有以下几种方案:

  • 2PC(两阶段提交)方案
  • TCC (Try-Confirm-Cancel)方案
  • 可靠消息最终一致性方案
  • 尽最大努力通知方案

下面对每一种解决方案做一个简要的概述:

【a】2PC两阶段提交方案

两个阶段主要做的工作如下:

  • 第一阶段:为资源上锁,预留资源;
  • 第二阶段:协调者向所有的参与者发送“正式提交”的命令,参与者完成正式提交;

原理图大概如下:

分布式事务处理demo 分布式事务处理方案_Server

工作流程:

【a】第一阶段:
1、事务协调者询问所有事务参与者,是否可以进行提交;
2、各个事务参与者进行事务执行的准备工作:如:为资源上锁,预留资源,并且相应事务协调者“可以提交”或者“不可以提交”	

【b】第二阶段:
1、假设所有事务参与者都OK,都可以进行提交操作,那么事务协调者发出“正式提交”命令,所有参与者完成正式提交,并释放所有资源,然后回应“完成”;
2、假设有任意一个事务参与者回应“不可以进行提交”,那么事务协调者需要向所有参与者发送“事务回滚”命令,并释放所有锁定的资源,然后回应“回滚完成”;

2PC阶段提交的缺点:

  • 锁定资源的时间较长,在高并发环境下,吞吐量不高;
  • 存在单点故障问题,假设事务协调者发生宕机等现象,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作;
  • 可能产生数据不一致问题,比如事务参与者A执行完Commit操作之后,这时候协调者发生了故障,导致事务参与者B并没有执行Commit操作。

 

【b】TCC (Try-Confirm-Cancel)方案

TCC方案其实是两阶段提交的一种改进,将整个业务逻辑的每个分支显式的分成了预处理Try、确认 Confirm、撤销Cancel三个操作。

三个阶段主要工作如下:

  • Try :做业务检查(一致性)及资源预留(隔离),此阶段仅是一个初步操作,它和后续的Confirm 一起才能 真正构成一个完整的业务逻辑;
  • Confirm :确认提交,Try阶段所有分支事务执行成功后开始执行 Confirm。通常情况下,采用TCC则 认为 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。若Confirm阶段真的出错了,需引 入重试机制或人工处理;
  • Cancel 阶段:是在业务执行错误需要回滚的状态下执行分支事务的业务取消,预留资源释放。通常情况下,采 用TCC则认为Cancel阶段也是一定成功的。若Cancel阶段真的出错了,需引入重试机制或人工处理;

分支事务成功图:

 

分布式事务处理demo 分布式事务处理方案_分布式事务处理demo_02

分支事务失败图:

分布式事务处理demo 分布式事务处理方案_分布式事务_03

目前常见的TCC解决方案有:tcc-transaction、Hmily、ByteTCC、EasyTransaction几个框架,这几个在GitHub上面stars相对较多,有兴趣的小伙伴可以在GitHub上面搜索一下进行学习。

TCC方案的缺点:很多事务的处理逻辑需要应用自己编码实现,复杂且开发量大

 

【c】可靠消息最终一致性方案

此方案是利用消息中间件完成,,如下图:

事务发起方(消息生产方)将消息发给消息中间件,事务参与方从消息中间件接收消息,事务发起方和消息中间件 之间,事务参与方(消息消费方)和消息中间件之间都是通过网络通信,由于网络通信的不确定性会导致分布式事务问题。

 

分布式事务处理demo 分布式事务处理方案_Server_04

我们通常使用RocketMQ事务消息方案来解决分布式事务。RocketMQ 是一个来自阿里巴巴的分布式消息中间件。

 

执行流程如下:

为方便理解我们以账户注册送积分的例子来描述整个流程。

 

Producer 即MQ发送方,即用户服务,负责新增用户。MQ订阅方即消息消费方,即积分服务,负责 新增积分。

 

  • 1、Producer 发送事务消息 Producer (MQ发送方)发送事务消息至MQ Server,MQ Server将消息状态标记为Prepared(预备状态),注意此时这条消息消费者(MQ订阅方)是无法消费到的。 本例中,Producer 发送 ”增加积分消息“ 到MQ Server。
  • 2、MQ Server回应消息发送成功。 MQ Server接收到Producer 发送给的消息则回应发送成功,表示MQ已接收到消息。
  • 3、Producer 执行本地事务 Producer 端执行业务代码逻辑,通过本地数据库事务控制。 本例中,Producer 执行添加用户操作。
  • 4-(1)、若Producer 本地事务执行成功则自动向MQServer发送commit消息,MQ Server接收到commit消息后将”增加积分消息“ 状态标记为可消费,此时MQ订阅方(积分服务)即正常消费消息;
  • 4-(2)、若Producer 本地事务执行失败则自动向MQServer发送rollback消息,MQ Server接收到rollback消息后 将删除”增加积分消息“ 。 MQ订阅方(积分服务)消费消息,消费成功则向MQ回应ack,否则将重复接收消息。这里ack默认自动回应,即 程序执行正常则自动回应ack。
  • 5、事务回查 如果执行Producer端本地事务过程中,执行端挂掉,或者超时,MQ Server将会不停的询问同组的其他 Producer 来获取事务执行状态,这个过程叫事务回查。MQ Server会根据事务回查结果来决定是否投递消息。

 

【d】尽最大努力通知方案

本方案是利用MQ的ack机制由MQ向接收通知方发送通知。

原理图大概如下:

 

分布式事务处理demo 分布式事务处理方案_协调者_05

流程如下:

  • 1、发起通知方将通知发给MQ。 使用普通消息机制将通知发给MQ。 注意:如果消息没有发出去可由接收通知方主动请求发起通知方查询业务执行结果。
  • 2、接收通知方监听 MQ。
  • 3、接收通知方接收消息,业务处理完成回应ack。
  • 4、接收通知方若没有回应ack则MQ会重复通知。 MQ会按照间隔1min、5min、10min、30min、1h、2h、5h、10h的方式,逐步拉大通知间隔 (如果MQ采用 rocketMq,在broker中可进行配置),直到达到通知要求的时间窗口上限。
  • 5、接收通知方可通过消息校对接口来校对消息的一致性。