分布式事务之TCC模型 原理解析

本文深入剖析了TCC分布式事务的运行机制,通过具体代码示例详细解释了TCC架构中核心组件的作用与交互流程,包括事务拦截器、资源协调器及事务上下文的构建与管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近学习了分布式框架TCC,为了自己不忘记TCC的运行方式,特次记录下TCC架构源码解析。

TCC的概念跟系统架构不多描述,不懂的直接百度。

先简单的写下TCC架构的代码用例:方法A为主服务,是调用者。

 @Compensable(confirmMethod = "confirmA",cancelMethod = "cancelA")
 @Transactional
 public void tryA(String args) throws Exception{

        //执行try操作的代码 对数据进行加锁操作
          。。。
        //远程调用方法B 
        B(null,args);

        // 远程调用方法C
        C(null,args)
        
   }
@Transactional
 public void confirmA(String args){

    //真正执行资源业务操作的代码
}
@Transactional
 public void cancelA(String args){

    //对加锁资源进行释放操作
}

远程调用方法B:

@Compensable(confirmMethod = "confirmB",cancelMethod = "cancelB")
@Transactional
public void tryB(TransactionContext transactionContext, String[] args ) {
  //对业务资源进行加锁		
			
}

@Transactional
public void confirmB(TransactionContext transactionContext, String[] args ) {
  //进行业务执行	
			
}

@Transactional
public void cancelB(TransactionContext transactionContext, String[] args ) {
  //对加锁的业务资源进行解锁	
			
}

远程调用方法C:

@Compensable(confirmMethod = "confirmC",cancelMethod = "cancelC")
@Transactional
public void tryC(TransactionContext transactionContext, String[] args ) {
  //对业务资源进行加锁		
			
}

@Transactional
public void confirmC(TransactionContext transactionContext, String[] args ) {
  //进行业务执行	
			
}

@Transactional
public void cancelC(TransactionContext transactionContext, String[] args ) {
  //对加锁的业务资源进行解锁	
			
}

作为参数的TransactionContext 是框架中自定义 事务上下文类,主要定义了事务id(全局事务id,分支事务id)跟事务状态字段,代码如下:

public class TransactionContext implements Serializable {

    private static final long serialVersionUID = -8199390103169700387L;
    
    //事务Id
    private TransactionXid xid;

    //事务状态
    private int status;

    /**
     * 附加属性.
     */
    private Map<String, String> attachments = new ConcurrentHashMap<String, String>();

  /**
     * 参与者列表.
     */
    private List<Participant> participants = new ArrayList<Participant>();


    public TransactionContext() {

    }

    /**
     * 构建事务上下文对像.
     * @param xid
     * @param status
     */
    public TransactionContext(TransactionXid xid, int status) {
        this.xid = xid;
        this.status = status;
    }

    ...//get set方法

}

TransactionXid 类代码如下:

public class TransactionXid implements Xid, Serializable {

    private static final long serialVersionUID = -6817267250789142043L;
    
    /**
     * XID 的格式标识符
     */
    private int formatId = 1;

    /**
     * 全局事务ID.
     */
    private byte[] globalTransactionId;

    /**
     * 分支限定符.
     */
    private byte[] branchQualifier;

    public TransactionXid() {
        globalTransactionId = uuidToByteArray(UUID.randomUUID());
        branchQualifier = uuidToByteArray(UUID.randomUUID());
    }

    public TransactionXid(byte[] globalTransactionId) {
        this.globalTransactionId = globalTransactionId;
        branchQualifier = uuidToByteArray(UUID.randomUUID());
    }

    public TransactionXid(byte[] globalTransactionId, byte[] branchQualifier) {
        this.globalTransactionId = globalTransactionId;
        this.branchQualifier = branchQualifier;
    }

  ...//剩下的不重要
}

说完了应用代码实例 跟 TransactionContext 事务上下文类。现在开始进入主题!。

TCC架构最重要的两个拦截器

<!-- 可补偿事务拦截器 -->
    <bean id="compensableTransactionInterceptor"
          class="org.mengyun.tcctransaction.interceptor.CompensableTransactionInterceptor">
        <property name="transactionConfigurator" ref="tccTransactionConfigurator"/>
    </bean>

	<!-- 资源协调拦截器 -->
    <bean id="resourceCoordinatorInterceptor"
          class="org.mengyun.tcctransaction.interceptor.ResourceCoordinatorInterceptor">
        <property name="transactionConfigurator" ref="tccTransactionConfigurator"/>
    </bean>

	<!-- TCC补偿切面 --> <!--拦截带有@annotation(org.mengyun.tcctransaction.Compensable)的方法-->
    <bean id="tccCompensableAspect" class="org.mengyun.tcctransaction.spring.TccCompensableAspect">
        <property name="compensableTransactionInterceptor" ref="compensableTransactionInterceptor"/>
    </bean>

	<!-- TCC事务上下文切面 --><!--拦截execution(public**(org.mengyun.tcctransaction.api.TransactionContext,..))或者@annotation(org.mengyun.tcctransaction.Compensable)-->
    <bean id="transactionContextAspect" class="org.mengyun.tcctransaction.spring.TccTransactionContextAspect">
        <property name="resourceCoordinatorInterceptor" ref="resourceCoordinatorInterceptor"/>
    </bean>

可补偿事务拦截器:org.mengyun.tcctransaction.interceptor.CompensableTransactionInterceptor 用来保证事务的正确执行,。

资源协调拦截器:org.mengyun.tcctransaction.interceptor.ResourceCoordinatorInterceptor 用来保证资源的正确调用。

在tryA被执行的时候,由于tryA方法带有@Compensable标签,所以先被TccCompensableAspect切面拦截

 

/**
     * 定义环绕通知(在一个方法执行之前和执行之后运行,第一个参数必须是 ProceedingJoinPoint类型,pjp将包含切点拦截的方法的参数信息)
     * @param pjp
     * @throws Throwable
     */
    @Around("compensableService()")
    public Object interceptCompensableMethod(ProceedingJoinPoint pjp) throws Throwable {
    	LOG.debug("==>interceptCompensableMethod");
        return compensableTransactionInterceptor.interceptCompensableMethod(pjp);
    }

继续看,CompensableTransactionInterceptor 这个时候开始发挥作用。

/**
     * 拦截补偿方法.
     * @param pjp
     * @throws Throwable
     */
    public Object interceptCompensableMethod(ProceedingJoinPoint pjp) throws Throwable {

    	// 从拦截方法的参数中获取事务上下文 /这个时候还没有事务上下文 所以transactionContext  = =null
        TransactionContext transactionContext = CompensableMethodUtils.getTransactionContextFromArgs(pjp.getArgs());
        
        // 计算可补偿事务方法类型 这里根据是否有注解 跟事务上下文是否为空判断 该方法类型
        MethodType methodType = CompensableMethodUtils.calculateMethodType(transactionContext, true);
        


        logger.debug("==>interceptCompensableMethod methodType:" + methodType.toString());

        switch (methodType) {
            case ROOT:
                return rootMethodProceed(pjp); // 主事务方法的处理(没有transactionContext参数)
            case PROVIDER:
                return providerMethodProceed(pjp, transactionContext); // 服务提供者事务方法处理
            default:
                return pjp.proceed(); // 其他的方法都是直接执行
        }
    }

由于transactionContext == null 而且 方法上标有注解 所以methodType == ROOT,代表tryA是根方法,也就是发起者所以查看rootMethodProceed(pjp);

/**
     * 主事务方法的处理.
     * @param pjp
     * @throws Throwable
     */
    private Object rootMethodProceed(ProceedingJoinPoint pjp) throws Throwable {
    	logger.debug("==>rootMethodProceed");

        transactionConfigurator.getTransactionManager().begin(); // 事务开始(创建事务日志记录,并在当前线程缓存该事务日志记录)
        //这一步创建一个status 为TRYING 的transactionType为ROOT事务上下文Transaction对象
        //并将该Transaction保存到数据库 放置当前线程变量中

        Object returnValue = null; // 返回值
        try {
        	
           logger.debug("==>rootMethodProceed try begin");
            returnValue = pjp.proceed();  // Try (开始执行被拦截的方法,或进入下一个拦截器处理逻辑)
            logger.debug("==>rootMethodProceed try end");
            
        } catch (OptimisticLockException e) {
            logger.warn("==>compensable transaction trying exception.", e);
            throw e; //do not rollback, waiting for recovery job
        } catch (Throwable tryingException) {
            logger.warn("compensable transaction trying failed.", tryingException);
            transactionConfigurator.getTransactionManager().rollback();
            throw tryingException;
        }

        logger.debug("===>rootMethodProceed begin commit()");
        transactionConfigurator.getTransactionManager().commit(); // Try检验正常后提交(事务管理器在控制提交):Confirm

        return returnValue;
    }

注意 这个时候执行到  returnValue = pjp.proceed();之前;

当前TCC事务上下文为:

执行到  returnValue = pjp.proceed(),会被TccTransactionContextAspect拦截 ,执行以下方法。

 /**
     * 定义环绕通知(在一个方法执行之前和执行之后运行,第一个参数必须是 ProceedingJoinPoint类型,方法的调用者得到的返回值就是环绕通知返回的值)
     * @param pjp
     * @throws Throwable
     */
    @Around("transactionContextCall()")
    public Object interceptTransactionContextMethod(ProceedingJoinPoint pjp) throws Throwable {
    	LOG.debug("==>interceptTransactionContextMethod(ProceedingJoinPoint pjp)");
        return resourceCoordinatorInterceptor.interceptTransactionContextMethod(pjp);
    }

然后看资源协调拦截器resourceCoordinatorInterceptor.interceptTransactionContextMethod(pjp)方法:

/**
     * 拦截事务上下文方法.
     * @param pjp
     * @throws Throwable
     */
    public Object interceptTransactionContextMethod(ProceedingJoinPoint pjp) throws Throwable {
    	LOG.debug("==>interceptTransactionContextMethod(ProceedingJoinPoint pjp)");
    	// 获取当前事务 
        Transaction transaction = transactionConfigurator.getTransactionManager().getCurrentTransaction();
        
        // Trying(判断是否Try阶段的事务)
        if (transaction != null && transaction.getStatus().equals(TransactionStatus.TRYING)) {
        	LOG.debug("==>TransactionStatus:" + transaction.getStatus().toString());
        	// 从参数获取事务上下文  从tryA参数中获取到的transactionContext  还是null
            TransactionContext transactionContext = CompensableMethodUtils.getTransactionContextFromArgs(pjp.getArgs());
            // 获取事务补偿注解
            Compensable compensable = getCompensable(pjp);
            // 计算方法类型 methodType  == root
            MethodType methodType = CompensableMethodUtils.calculateMethodType(transactionContext, compensable != null ? true : false);
            LOG.debug("==>methodType:" + methodType.toString());
            
            switch (methodType) {
                case ROOT:
                    generateAndEnlistRootParticipant(pjp); // 生成和登记根参与者
                    break;
                case CONSUMER:
                    generateAndEnlistConsumerParticipant(pjp); // 生成并登记消费者的参与者
                    break;
                case PROVIDER:
                    generateAndEnlistProviderParticipant(pjp); // 生成并登记服务提供者的参与者
                    break;
            }
        }
        
        LOG.debug("==>pjp.proceed(pjp.getArgs())");
        return pjp.proceed(pjp.getArgs()); // 开始执行被拦截的方法,或进入下一个拦截器处理逻辑
    }

查看 generateAndEnlistRootParticipant(pjp); 他是生成一个包含confirmA跟cancelA的Participant,并将其加入到transaction中,保存到A方法所指定的数据库。

/**
     * 生成和登记根参与者.
     * @param pjp
     * @return
     */
    private Participant generateAndEnlistRootParticipant(ProceedingJoinPoint pjp) {
    	LOG.debug("==>generateAndEnlistRootParticipant(ProceedingJoinPoint pjp)");
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        Compensable compensable = getCompensable(pjp);
        String confirmMethodName = compensable.confirmMethod(); // 确认方法
        String cancelMethodName = compensable.cancelMethod(); // 取消方法

        Transaction transaction = transactionConfigurator.getTransactionManager().getCurrentTransaction(); // 获取当前事务

        TransactionXid xid = new TransactionXid(transaction.getXid().getGlobalTransactionId()); // 使用全局事务ID和新的分支事务限定符号,生成新的事务Xid
        LOG.debug("==>TransactionXid:" + TransactionXid.byteArrayToUUID(xid.getGlobalTransactionId()).toString()
        		+ "|" + TransactionXid.byteArrayToUUID(xid.getBranchQualifier()).toString());

        // 获取到目标类(最好做成独立的类)
        Class targetClass = ReflectionUtils.getDeclaringType(pjp.getTarget().getClass(), method.getName(), method.getParameterTypes());

        // 构建确认方法的提交上下文(相同的参数)
        InvocationContext confirmInvocation = new InvocationContext(targetClass,
                confirmMethodName,
                method.getParameterTypes(), pjp.getArgs());
        
        // 构建取消方法的提交上下文(相同的参数)
        InvocationContext cancelInvocation = new InvocationContext(targetClass,
                cancelMethodName,
                method.getParameterTypes(), pjp.getArgs());

        // 构建参与者对像
        Participant participant =
                new Participant(
                        xid,
                        new Terminator(confirmInvocation, cancelInvocation));

        transaction.enlistParticipant(participant); // 加入参与者

        TransactionRepository transactionRepository = transactionConfigurator.getTransactionRepository();
        transactionRepository.update(transaction); // 更新事务信息(加入了事务参与者,包含了触发confirm或cancel方法的参数信息)

        return participant;
    }

此时事务信息上下文:

 

继续往下执行pjp.proceed(pjp.getArgs());

这时候执行到tryB方法,方法B参数中含有Transaction类,所以被TccCompensableAspect切面拦截,执行

/**
     * 拦截事务上下文方法.
     * @param pjp
     * @throws Throwable
     */
    public Object interceptTransactionContextMethod(ProceedingJoinPoint pjp) throws Throwable {
    	LOG.debug("==>interceptTransactionContextMethod(ProceedingJoinPoint pjp)");
    	// 获取当前事务 
        Transaction transaction = transactionConfigurator.getTransactionManager().getCurrentTransaction();
        
        // Trying(判断是否Try阶段的事务)
        if (transaction != null && transaction.getStatus().equals(TransactionStatus.TRYING)) {
        	LOG.debug("==>TransactionStatus:" + transaction.getStatus().toString());
        	// 从参数获取事务上下文  从tryA参数中获取到的transactionContext  还是null
            TransactionContext transactionContext = CompensableMethodUtils.getTransactionContextFromArgs(pjp.getArgs());
            // 获取事务补偿注解
            Compensable compensable = getCompensable(pjp);
            // 计算方法类型 
            MethodType methodType = CompensableMethodUtils.calculateMethodType(transactionContext, compensable != null ? true : false);
            LOG.debug("==>methodType:" + methodType.toString());
            
            switch (methodType) {
                case ROOT:
                    generateAndEnlistRootParticipant(pjp); // 生成和登记根参与者
                    break;
                case CONSUMER:
                    generateAndEnlistConsumerParticipant(pjp); // 生成并登记消费者的参与者
                    break;
                case PROVIDER:
                    generateAndEnlistProviderParticipant(pjp); // 生成并登记服务提供者的参与者
                    break;
            }
        }
        
        LOG.debug("==>pjp.proceed(pjp.getArgs())");
        return pjp.proceed(pjp.getArgs()); // 开始执行被拦截的方法,或进入下一个拦截器处理逻辑
    }

这个时候methodType为CONSUMER,执行generateAndEnlistConsumerParticipant(pjp),生成提供服务者的参与者,此时事务上下文:

开始远程执行tryB方法,远程方法tryB被执行的时候,由于有注释和transaction参数,所以一样会被两个切面拦截,由于过程一样,第一个切面拦截时,其methodType 为PROVIDER,执行providerMethodProceed(pjp, transactionContext);方法

 

private Object providerMethodProceed(ProceedingJoinPoint pjp, TransactionContext transactionContext) throws Throwable {
    	
    	logger.debug("==>providerMethodProceed transactionStatus:" + TransactionStatus.valueOf(transactionContext.getStatus()).toString());

        switch (TransactionStatus.valueOf(transactionContext.getStatus())) {
            case TRYING:
            	logger.debug("==>providerMethodProceed try begin");
            	// 基于全局事务ID扩展创建新的分支事务,并存于当前线程的事务局部变量中.
                transactionConfigurator.getTransactionManager().propagationNewBegin(transactionContext);
                logger.debug("==>providerMethodProceed try end");
                return pjp.proceed(); // 开始执行被拦截的方法,或进入下一个拦截器处理逻辑
            case CONFIRMING:
                try {
                	logger.debug("==>providerMethodProceed confirm begin");
                	// 找出存在的事务并处理.
                    transactionConfigurator.getTransactionManager().propagationExistBegin(transactionContext);
                    transactionConfigurator.getTransactionManager().commit(); // 提交
                    logger.debug("==>providerMethodProceed confirm end");
                } catch (NoExistedTransactionException excepton) {
                    //the transaction has been commit,ignore it.
                }
                break;
            case CANCELLING:
                try {
                	logger.debug("==>providerMethodProceed cancel begin");
                    transactionConfigurator.getTransactionManager().propagationExistBegin(transactionContext);
                    transactionConfigurator.getTransactionManager().rollback(); // 回滚
                    logger.debug("==>providerMethodProceed cancel end");
                } catch (NoExistedTransactionException exception) {
                    //the transaction has been rollback,ignore it.
                }
                break;
        }

        Method method = ((MethodSignature) (pjp.getSignature())).getMethod();

        return ReflectionUtils.getNullValue(method.getReturnType());
    }

此时事务上下文的status为TRYING,远程方法B这边跟局传过来的transactionContext中的全局事务Id开启一个新的事务。

继续被TccTransactionContextAspect切面拦截,生成生成并登记服务提供者的参与者,此时事务上下文:

然后远程服务tryB被真正执行完,回到tryA方法,开始执行tryC方法,同样的经过切面拦截,事务上下文变为:

事务上下文构建完之后,这个时候 A方法中的切面拦截方法还没有结束,才刚刚执行完pjp.proceed();方法

然后开始往下执行,transactionConfigurator.getTransactionManager().commit();

 /**
     * 提交.
     */
    public void commit() {
    	LOG.debug("==>TransactionManager commit()");
        Transaction transaction = getCurrentTransaction();

        transaction.changeStatus(TransactionStatus.CONFIRMING);
        LOG.debug("==>update transaction status to CONFIRMING");
        transactionConfigurator.getTransactionRepository().update(transaction);

        try {
        	LOG.info("==>transaction begin commit()");
            transaction.commit();
            transactionConfigurator.getTransactionRepository().delete(transaction);
        } catch (Throwable commitException) {
            LOG.error("compensable transaction confirm failed.", commitException);
            throw new ConfirmingException(commitException);
        }
    }

执行  transaction.commit();操作

 /**
     * 事务提交(包含此事务的所有参与者的逐个提交,在TransactionManager中被调用).
     */
    public void commit() {
    	LOG.debug("==>Transaction.commit()");
        for (Participant participant : participants) {
            participant.commit();
        }
    }

  participant.commit();方法就是执行各个participant的confirm方法,所以这个时候事务上下文中所有participant的confirm方法会被执行:

各个的confirm操作执行完之后,业务代码基本都执行完了,最后执行   transactionConfigurator.getTransactionRepository().delete(transaction);

将数据库中保存的transaction上下文删除。

如果在执行tryA,tryB,tryC方法的时候出现异常:

这里有一个事务回滚的操作,即事务上下文中的各个participant调用cancel方法,来保分布式证事务的一致性。

下一篇记录TCC事务如果confirmA,confirmB执行了,但是confirmC失败之后的补偿操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值