tx-lcn探险

一、描述:

  随着分布式服务的到来,分布式事务必然也就成为分布式的重点,因此了解tx-lcn很有必要

二、tx-lcn的TC与TM的交互图:

  注:其中事务发起方为TC-A,而TC-B、TC-C为事务参与方,TM为事务处理器:

  

   交互描述:

   1、TC-A向TM发送创建组请求
   2、TC-B向TM发送加入组请求
   3、TC-C向TM发送加入组请求
   4、TC-B将调用结果返回TC-A
   5、TC-B将事务状态发送到TM
   6、TC-C将调用结果返回TC-A
   7、TC-C将事务状态发送到TM
   8、TC-A向TM发送事务完成通知
   9、TM根据事务情况,分别向TC-A,TC-B,TC-C发送事务通知

二、各注解的适用: 

  1、@LCNTransaction:(一般用于mysql或oracle,在于支持事务场景)
    代理本地connection连接,使用数据库本身提供的commit或者rollback完成事务的提交或者回滚
  2、@TCCTransaction:(一般用于redis,memcache)
    不代理本地connection连接,以callback指定类,confirm、cancel指定类内的方法完成事务的提交或者回滚
  3、@TXCTransaction:(一般用于mysql或oracle,在于不支持事务场景)
    代理本地connection连接,将操作可影响到的数据先查询缓存,通过TM的事务通知完成删除备份数据或者恢复备份数据
  4、@TXTransaction:
    以上三个注解的抽象版,默认为@LCNTransaction
  注:分布式事务的组合可以是LCN+LCN+LCN,TCC+TCC+TCC,TXC+TXC+TXC,LCN+TCC+TXC或者LCN+LCN(LCN+TCC+TXC)+TXC,也就是说可以任意组合、嵌套;

三、TM交互步骤:

  TM的交互分三层:

  第一层为接受TC的调用通知:
    ServerRpcAnswer:callback();
  第二层初始化通信数据:
    (1):QueryTMClusterExecuteService
      查询已缓存的TM地址
    (2):InitClientService
      绑定TC的通信信息
  第三层为TM和TC的事务交互层:
    (1):CreateGroupExecuteService
      创建事务组,开始分布式事务
    (2):AcquireDTXLockExecuteService
      获取分布式锁(一般用于TXC类型的事务)
    (3):JoinGroupExecuteService
      加入事务组
    (4):NotifyGroupExecuteService
      通知TC事务的状态
    (5):CleanInvalidTMExecuteService、
      通知TC要求清空事务日志
    (6):ReleaseDTXLockExecuteService
      释放分布式锁(和获取搭配使用)
    (7):WriteTxExceptionExecuteService
      将事务异常写入本地日志

四、TC交互步骤: 

  TC的交互分三层:
  第一层为获取注解层:
    如:TransactionAspect类的方法:
      (1):代理标注@LCNTransaction注解的方法
        @Pointcut("@annotation(com.codingapi.txlcn.tc.annotation.TxTransaction)")
        public void txTransactionPointcut() {}
      (2):代理标注@TCCTransaction注解的方法
        @Pointcut("@annotation(com.codingapi.txlcn.tc.annotation.LcnTransaction)")
        public void lcnTransactionPointcut() {}
      (3):代理标注@TXCTransaction注解的方法
        @Pointcut("@annotation(com.codingapi.txlcn.tc.annotation.TxcTransaction)")
        public void txcTransactionPointcut() {}
      (4):代理标注@TXTransaction注解的方法
        @Pointcut("@annotation(com.codingapi.txlcn.tc.annotation.TccTransaction)")
        public void tccTransactionPointcut() {}
  第二层为代理connection层:
    如:LcnTransactionResourceProxy类的方法:proxyConnection
    如:TccTransactionResourceProxy类的方法:proxyConnection
    如:TxcTransactionResourceProxy类的方法:proxyConnection
  第三层为rpc交互层:
    获取事务管理类:
      (1):如TC-A所示处于事务最外层:
        使用(TCN|TCC|TXC)StartingTransaction作为事务的代理层包含三部分内容:
        preBusinessCode:创建组并代理本地connection,如createGroup,makeProxy
        onBusinessCodeError|onBusinessCodeSuccess:记录或清空本地事务状态
        postBusinessCode:发送事务状态通知,如notifyGroup
      (2):如TC-B、TC-C所示处于事务内层(这里假设TC-A、TC-B、TC-C处于同一个应用系统):
        使用(TCN|TCC|TXC)RunningTransaction作为事务的代理层包含三部分内容:
        preBusinessCode:代理本地connection,如makeProxy
        onBusinessCodeError:清空本地事务日志记录
        onBusinessCodeSuccess:加入组,如joinGroup
    进行交互:
      交互执行类:DTXServiceExecutor:
        交互方法transactionRunning:
        //被代理方法执行前操作
        dtxLocalControl.preBusinessCode(info);
        //调用被代理的方法
        dtxLocalControl.doBusinessCode(info);
        //代理方法执行后操作
        dtxLocalControl.onBusinessCodeSuccess(info, result);
        //被代理执行出错操作
        dtxLocalControl.onBusinessCodeError(info, e);
        //整个交互完成后操作
        dtxLocalControl.postBusinessCode(info);
     接收TM的调用:ClientRpcAnswer:callback()
      //或是通知提交事务,或是通知回滚事务,或是通知删除日志
  注:参与分布式事务的每个服务需要分别建组,如果当前线程变量里面已经存有组,加入即可,否则需要创建组并存储到线程变量内

五、疑问以及解答

  1、如何保证分布式服务的多个参与方同属于一个事务组
    在主参与方创建事务组groupId,并保存到redis中,后面每个事务参与者都携带该groupId:统一管理事务组
  2、若在一个分布式事务中多次调用同一应用的同一方法,如何做区分
    每个事务参与方都有groupId(事务组)+modeId(模块标识)+unitId(方法全称)+transactionType(事务类型)做唯一性区分:如果业务合理应该不会冲突
  3、最终分布式事务能不能完成提交或者回滚
    最终分布式事务的决断在TM,而TM只会向同一groupId事务参与方发送通知,也就是说只支持同一个服务内的多个参与方的分布式事务:改源码
  4、Txc类型的事务能不能嵌套
    经过简单试验,确实可以

五、demo地址:https://gitee.com/lswater/tx-lcn-demo

猜你喜欢

转载自www.cnblogs.com/lswater/p/11863336.html