分布式事务TX-LCN原理分析

分布式事务框架 TX-LCN原理分析

分布式事务的多种形式

  • XA 基于数据库实现,对数据库是否支持依赖较重。
  • TCC 主要基于业务代码控制,分为try/commit/cancel三阶段。业务侵入强,开发和维护难度大。
  • TXC 基于对SQL的分析,保存SQL执行所影响到的数据快照,在失败后进行回滚。业务侵入低,对SQL兼容要求高,部分SQL不支持。
  • LCN 通过代理数据库连接对事务进行控制。通过静态代理方式包装原来的数据库连接,根据事务状态控制提交和回滚。业务侵入低,但是因为需要代理数据库连接,必须有本地事务,同时会增加连接占用时间。

一、TX-LCN介绍

  • LCN名字来源:锁定事务单元(Lock)、确认事务模块状态(Confirm)、通知事务(Notify)

  • TX-LCN是一款事务协调框架,其本身不操作事务,而是基于对事务的协调从而达到事务一致性的效果。

  • 在一个分布式系统下存在多个模块,需要协调完成一次业务,那就存在一次业务事务下可能横跨多种数据源节点的可能。TX-LCN可以解决这样的问题:存在服务模块A、B、C。A模块是MySQL为数据源的服务,B模块是基于Redis为数据源的服务,C模块是基于MongoDB为数据源的服务。若要解决他们的事务一致性就需要针对不同的节点采用不同的方案,并同意协调完成分布式事务的处理。

  • img

  • 方案:采用TX-LCN分布式事务框架,则可以将模块A使用LCN模式,B、C采用TCC模式完美解决。

  • TX-LCN主要分为Tx-Client和Tx-Manager两个模块,TC作为微服务下的依赖,是事务的发起方。TM是独立的服务,是事务的控制方,用于协调事务。

  • img

  • 核心步骤如下:

    • 创建事务组

      是指在事务发起方开始执行业务代码前先调用TxManager创建事务组对象,然后拿到事务标志GroupId的过程

    • 加入事务组

      是指参与方在执行完业务方法后,将该模块的事务信息通知给TxManager的操作

    • 通知事务组

      在发起方执行完业务代码后,将发起方执行结果状态通知给TxManager,TxManager根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方。

二、事务协调机制

  • TX-LCN框架通过方法上配置的事务注解信息来获取当前事务类型,封装事务参数,执行业务代码,清理线程变量等等。具体的事务执行流程则交由DTXServiceExecutor.transationRunning处理,再其内部再根据事务类型和发起方的不同进行不同的处理。

    transationRunning中保存事务执行日志、处理事务:执行业务前、执行业务、执行业务后、最后finally。

    根据封装的事务参数获取DTXLocalControl顺序调用preBusinessCode、doBusinessCode、onBusinessCodeSuccess or onBusinessCodeError、postBusinessCode。事务发起方的preBusinessCode通过netty调用TxManager创建事务组,参与方则在onBusinessCodeSuccess中加入事务组。

    参与方加入事务组后会创建一个延迟线程检测DTX事务状态,通过TxManager查询txException的信息来获取最终的state,同时也可以等待netty通知得到state信息。当超时没有得到通知时才会通过延迟线程得到state,得到通知后会清除对应的延迟线程。最后通过state提交或者回滚事务。

    在整个业务代码执行完毕之后,发起方调用postBusinessCode方法,通知事务组最后的执行状态,TxManager服务端获取事务组的状态,并下发事务状态通知到各个模块(通过netty进行发送),并返回最终的state给发起方,发起方再根据state提交或者回滚事务并清理本地事务。

  • DTXLocalContext\TracingContext\TCGlobalContext 共同管理当前线程下事务的一些信息。包括groupId、unitId、txContext等等关键信息

  • 在一个DTX下每个模块中都有且只有一个全局上下文TxContext,当不存在时会进行创建:

    如果一个调用链是这样的A->B->C 三个都是不同的模块则会为每个模块都构造新的TxContext,保证一个模块一个。

    如果三个是两个模块,即A是一个模块,B和C是一个模块根据调用链的情况肯定是C的用B的TxContext。

  • 多个模块事务发起和参与关系通过Tracings来协调,对RPC请求传递Header信息,内容为groupId和appMap,模块在后续处理中通过获取全局上下文获取是否拥有groupId来判断是否是参与方

业务模块A、B、C

正常提交

当业务全部正常提交时,A最后调用postBusinessCode,告知TxManager通知事务组最后状态,服务端会对B\C发起通知提交事务。

异常回滚

  1. 当A B C 中 C业务异常会直接抛出到B 同时B 抛出到A,最后B、C中直接本地回滚,A得到后也直接回滚,此时并没有任何参与方加入到事务组中,发送的通知也没有作用,因为仅仅成功后调用onBusinessCodeSuccess的才会加入到事务组中去。
  2. 当出现异常而A没有成功告知服务端,则会记录txException? 然后B\C通过延迟检测线程获取最后的事务状态

TODO 详细的异常处理