分布式事务 TCC-Transaction 源码分析 —— TCC 实现

本文主要基于 TCC-Transaction 1.2.3.3 正式版

  • 1. 概述

  • 2. TCC 原理

  • 3. TCC-Transaction 原理

  • 4. 事务与参与者

    • 4.1 事务

    • 4.2 参与者

  • 5. 事务管理器

    • 5.1 发起根事务

    • 5.2 传播发起分支事务

    • 5.3 传播获取分支事务

    • 5.4 提交事务

    • 5.5 回滚事务

    • 5.6 添加参与者到事务

  • 6. 事务拦截器

    • 6.1 Compensable

    • 6.2 可补偿事务拦截器

    • 6.3 资源协调者拦截器

  • 666. 彩蛋


友情提示:欢迎关注公众号【芋道源码】。?关注后,拉你进【源码圈】微信群和【芋艿】搞基嗨皮。

友情提示:欢迎关注公众号【芋道源码】。?关注后,拉你进【源码圈】微信群和【芋艿】】搞基嗨皮。

友情提示:欢迎关注公众号【芋道源码】。?关注后,拉你进【源码圈】微信群和【芋艿】】搞基嗨皮。


1. 概述

本文分享 TCC 实现。主要涉及如下三个 Maven 项目:

  • tcc-transaction-core :tcc-transaction 底层实现。

  • tcc-transaction-api :tcc-transaction 使用 API。

  • tcc-transaction-spring :tcc-transaction Spring 支持。

你行好事会因为得到赞赏而愉悦 
同理,开源项目贡献者会因为 Star 而更加有动力 
为 TCC-Transaction 点赞!传送门

OK,开始我们的第一段 TCC 旅程吧。

ps:笔者假设你已经阅读过《tcc-transaction 官方文档 —— 使用指南1.2.x》。

ps2:未特殊说明的情况下,本文事务指的是 TCC事务

2. TCC 原理

FROM https://support.hwclouds.com/devg-servicestage/zh-cn_topic_0056814426.html 
TCC事务 
为了解决在事务运行过程中大颗粒度资源锁定的问题,业界提出一种新的事务模型,它是基于业务层面的事务定义。锁粒度完全由业务自己控制。它本质是一种补偿的思路。它把事务运行过程分成 Try、Confirm / Cancel 两个阶段。在每个阶段的逻辑由业务代码控制。这样就事务的锁粒度可以完全自由控制。业务可以在牺牲隔离性的情况下,获取更高的性能。

  • Try 阶段

    • 完成所有业务检查( 一致性 )

    • 预留必须业务资源( 准隔离性 )

    • Try :尝试执行业务

  • Confirm / Cancel 阶段:

    • 释放 Try 阶段预留的业务资源

    • Cancel 操作满足幂等性

    • 真正执行业务

    • 不做任务业务检查

    • Confirm 操作满足幂等性

    • Confirm :确认执行业务

    • Cancel :取消执行业务

    • Confirm 与 Cancel 互斥

整体流程如下图:

  • 红框部分功能由 tcc-transaction-core 实现:

    • 启动业务活动

    • 登记业务操作

    • 提交 / 回滚业务活动

  • 黄框部分功能由 tcc-transaction-http-sample 实现( 官方提供的示例项目 ):

    • Try 操作

    • Confirm 操作

    • Cancel 操作

与 2PC协议 比较

  • 位于业务服务层而非自愿层

  • 没有单独的准备( Prepare )阶段,Try 操作兼备自愿操作与准备能力

  • Try 操作可以灵活选择业务资源的锁定粒度

  • 较高开发成本

参考资料:

  • 《支付宝运营架构中柔性事务指的是什么?》

  • 《分布式事务的典型处理方式:2PC、TCC、异步确保和最大努力型》

3. TCC-Transaction 原理

在 TCC 里,一个业务活动可以有多个事务,每个业务操作归属于不同的事务,即一个事务可以包含多个业务操作。TCC-Transaction 将每个业务操作抽象成事务参与者,每个事务可以包含多个参与者

参与者需要声明 try / confirm / cancel 三个类型的方法,和 TCC 的操作一一对应。在程序里,通过 @Compensable 注解标记在 try 方法上,并填写对应的 confirm / cancel 方法,示例代码如下:

// try
@Compensable(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = MethodTransactionContextEditor.class)
public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {}

// confirm
public void confirmRecord(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {}

// cancel
public void cancelRecord(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {}
  • 在示例代码中,我们看到 TransactionContext,事务上下文,这个是怎么生成的呢?这里先卖一个关子。

TCC-Transaction 有两个拦截器,通过对 @Compensable AOP 切面( 参与者 try 方法 )进行拦截,透明化对参与者 confirm / cancel 方法调用,从而实现 TCC 。简化流程如下图:

第一个拦截器,可补偿事务拦截器,实现如下功能:

  • 在 Try 阶段,对事务的发起、传播。

  • 在 Confirm / Cancel 阶段,对事务提交或回滚。

  • 为什么会有对事务的传播呢?在远程调用服务的参与者时,会通过"参数"( 需要序列化 )的形式传递事务给远程参与者。

第二个拦截器,资源协调者拦截器,实现如下功能:

  • 在 Try 阶段,添加参与者到事务中。当事务上下文不存在时,进行创建。

实际拦截器对事务的处理会比上图复杂一些,在本文「6. 事务拦截器」详细解析。

在 TCC-Transaction 代码实现上,组件分层如下图:

本文按照如下顺序分享:

  • 「4. 事务拦截器」

  • 「5. 事务管理器」

  • 「6. 事务管理器」

内容是自下而上的方式分享,每个组件可以更加整体的被认识。当然这可能对你理解时产生一脸闷逼,所以推荐两种阅读方式:

  • 简读 x 1 + 深读 x 1

  • 倒着读,发现未分享的方法,全文检索该方法。

事务存储器在《TCC-Transaction 源码解析 —— 事务存储于恢复》详细解析。

事务恢复在《TCC-Transaction 源码解析 —— 事务恢复》详细解析。

4. 事务与参与者

在 TCC 里,一个事务( org.mengyun.tcctransaction.Transaction ) 可以有多个参与者( org.mengyun.tcctransaction.Participant )参与业务活动。类图关系如下( 打开大图 ):

4.1 事务

Transaction 实现代码如下

public class Transaction implements Serializable {
   

   private static final long serialVersionUID = 7291423944314337931L;

   /**
    * 事务编号
    */

   private TransactionXid xid;
   /**
    * 事务状态
    */

   private TransactionStatus status;
   /**
    * 事务类型
    */

   private TransactionType transactionType;
   /**
    * 重试次数
    */

   private volatile int retriedCount = 0;
   /**
    * 创建时间
    */

   private Date createTime = new Date();
   /**
    * 最后更新时间
    */

   private Date lastUpdateTime = new Date();
   /**
    * 版本号
    */

   private long version = 1;
   /**
    * 参与者集合
    */

   private List<Participant> participants = new ArrayList<Participant>();
   /**
    * 附带属性映射
    */

   private Map<String, Object> attachments = new ConcurrentHashMap<String, Object>();

   /**
    * 添加参与者
    *
    * @param participant 参与者
    */

   public void enlistParticipant(Participant participant) {
       participants.add(participant);
   }

   /**
    * 提交 TCC 事务
    */

   public void commit() {
       for (Participant participant : participants) {
           participant.commit();
       }
   }

   /**
    * 回滚 TCC 事务
    */

   public void rollback() {
       for (Participant participant : participants) {
           participant.rollback();
       }
   }
}
  • xid,事务编号( TransactionXid ),用于唯一标识一个事务。使用 UUID 算法生成,保证唯一性org.mengyun.tcctransaction.api.TransactionXid 实现 javax.transaction.xa.Xid 接口,实现代码如下:

    public class TransactionXid implements Xid, Serializable {
         
       private static final long serialVersionUID = -6817267250789142043L;
    /**
    * xid 格式标识符
    */

    private int formatId = 1;
    /**
    * 全局事务编号
    */

    private byte[] globalTransactionId;
    /**
    * 分支事务编号
    */

    private byte[] branchQualifier;
    }
    • TODO 为什么要继承 Xid 接口?

  • status,事务状态( TransactionStatus )。org.mengyun.tcctransaction.api.TransactionStatus 实现代码如下:

    public enum TransactionStatus {
         
       /**
        * 尝试中状态
        */

       TRYING(1),
       /**
        * 确认中状态
        */

       CONFIRMING(2),
       /**
        * 取消中状态
        */

       CANCELLING(3);
    private int id;
    }
  • transactionType,事务类型( TransactionType )。org.mengyun.tcctransaction.common.TransactionType 实现代码如下:

    public enum TransactionType {
         
    /**
    * 根事务
    */

    ROOT(1),
    /**
    * 分支事务
    */

    BRANCH(2);

    int id;
    }
    • 在「6.2 可补偿事务拦截器」有详细解析,可以看到看到这两种事务是如何发起。

  • retriedCount,重试次数。在 TCC 过程中,可能参与者异常崩溃,这个时候会进行重试直到成功或超过最大次数。在《TCC-Transaction 源码解析 —— 事务恢复》详细解析。

  • version,版本号,用于乐观锁更新事务。在《TCC-Transaction 源码解析 —— 事务存储器》详细解析。

  • attachments,附带属性映射。在《TCC-Transaction 源码解析 —— Dubbo 支持》详细解析。

  • 提供 #enlistParticipant() 方法,添加事务参与者。

  • 提供 #commit() 方法,调用参与者们提交事务。

  • 提供 #rollback() 方法,调用参与者回滚事务。

4.2 参与者

Participant 实现代码如下

public class Participant implements Serializable {
   

   private static final long serialVersionUID = 4127729421281425247L;

   /**
    * 事务编号
    */

   private TransactionXid xid;
   /**
    * 确认执行业务方法调用上下文
    */

   private InvocationContext confirmInvocationContext;
   /**
    * 取消执行业务方法
    */

   private InvocationContext cancelInvocationContext;
   /**
    * 执行器
    */

   private Terminator terminator = new Terminator();
   /**
    * 事务上下文编辑
    */

   Class<? extends TransactionContextEditor> transactionContextEditorClass;

   /**
    * 提交事务
    */

   public void commit() {
       terminator.invoke(new TransactionContext(xid, TransactionStatus.CONFIRMING.getId()), confirmInvocationContext, transactionContextEditorClass);
   }

   /**
    * 回滚事务
    */

   
  • 3
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值