分布式事务框架(seata1.5.0)源码分析-TCC模式

目录

TCC模式

Seata整体架构

事务控制相关概念

空回滚

幂等

防悬挂

GlobalTransactionScanner切面扫描

全局事务处理流程(GlobalTransactionalInterceptor)

全局事务注册

全局事务提交

全局事务回滚

一阶段分支事务Try处理流程(TccActionInterceptor)

分支注册&&上下文参数处理

tcc_fence_log

tcc_fence_log表结构

Try阶段插入tcc_fence_log表

二阶段处理流程

Commit处理流程

Rollback处理流程

TCC幂等&&空回滚&&防悬挂控制总结


TCC模式

整体是 两阶段提交 的模型。全局事务是由若干分支事务组成的,分支事务要满足 两阶段提交 的模型要求,即需要每个分支事务都具备3个方法:

  • Try:资源的检测和预留
  • Confirm:执行的业务操作提交;要求 Try 成功 Confirm 一定要能成功;
  • Cancel:预留资源释放

在一阶段执行 Try 方式,在二阶段提交执行 Confirm 方法,二阶段回滚执行 Cancel 方法。

Seata整体架构

Seata由核心三大模块组成:TM、RM 和 TC,其中TM和RM集成在我们的业务系统作为seata的客户端,而TC作为Seata的server端独立部署。

分布式事务框架(seata1.5.0)源码分析-TCC模式_第1张图片

  • TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,与客户端TM、RM通信,驱动全局事务提交或回滚。

  • TM (Transaction Manager) - 事务管理器:与服务端TC通信,开启全局事务、提交或回滚全局事务。

  • RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与TC通信以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

Seata中,一次事务的执行流程大体如下:

  1. 客户端TM开启一个全局事务,其实就是TM要向TC注册全局事务记录
  2. 按照业务的执行流程,客户端RM向TC注册分支事务,执行分支事务一阶段业务逻辑处理。
  3. 各分支事务一阶段完成,客户端TM 通知 TC 提交/回滚分布式事务
  4. Server端TC汇总所有的分支事务状态,决定事务分支是提交还是回滚
  5. Server端TC 通知所有 RM 提交/回滚 资源,事务二阶段结束

异常场景的处理:

  • TM发起注册全局事务:若注册失败或TC响应超时则抛出异常,流程结束
  • RM发起注册分支事务:若注册失败或TC响应超时则抛出异常,流程结束
  • 业务逻辑执行:若业务处理超时,即TM迟迟未向TC发起全局事务提交/回滚请求,TC检测到全局事务超时后,将禁止分支事务的注册,并发起全局事务回滚
  • TM发起全局事务回滚请求:若请求超时,这个时候可以直接忽略此超时异常。如果TC收到了回滚请求,那么正常回滚。如果TC没收到回滚请求,那么TC在检测到全局事务超时后,一样会发起全局回滚。
  • TM发起全局事务提交请求:若请求超时,如果TC没收到提交请求,那么TC在检测到全局事务超时后,会发起全局回滚,这时与业务代码的异常状态,事务是一致的。如果TC收到了提交请求,那么就可能出现事务不一致的状况。这个时候需要我们做定制化开发,比如说把这种全局事务提交超时的异常登记起来,并且server端提供一个查询接口供客户端回查全局事务的最终状态。

事务控制相关概念

空回滚

空回滚就是对于一个分布式事务,在没有调用 TCC 资源 Try 方法的情况下,调用了二阶段的 Cancel 方法,Cancel 方法需要识别出这是一个空回滚,然后直接返回成功。

出现原因:

  • Try超时(丢包)>> 分布式事务回滚触发cancel >> 未收到try、收到cancel

幂等

幂等就是对于同一个分布式事务的同一个分支事务,重复去调用该分支事务的第二阶段接口,因此,要求 TCC 的二阶段 Confirm 和 Cancel 接口保证幂等,不会重复使用或者释放资源。如果幂等控制没有做好,很有可能导致资损等严重问题。

出现原因:

  • 网络异常

防悬挂

Cancel 比 Try 接口先执行,出现的原因是 Try 由于网络拥堵而超时,事务管理器生成回滚,触发 Cancel 接口,而最终又收到了 Try 接口调用,但是 Cancel 比 Try 先到。按照前面允许空回滚的逻辑,回滚会返回成功,事务管理器认为事务已回滚成功,则此时的 Try 接口不应该执行,否则会产生数据不一致

出现原因:

  • Try超时(拥堵)>> 分布式事务回滚触发cancel >> Try到达

本文重点在于源码分析,不过多介绍分布式事务相关原理和概念,下面进入正题

GlobalTransactionScanner切面扫描

Seata全局事务发起、提交/回滚依赖于SpringAop,GlobalTransactionScanner则是aop的入口类,此类继承于AbstractAutoProxyCreator,重写了wrapIfNecessary方法,懂aop都知道,此方法通过添加interceptor到SpringAop的拦截器链中,生成代理对象来完成代理逻辑的处理。


public class GlobalTransactionScanner extends AbstractAutoProxyCreator
        implements ConfigurationChangeListener, InitializingBean, ApplicationContextAware, DisposableBean {

    @Override
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // do checkers
        if (!doCheckers(bean, beanName)) {
            return bean;
        }

        try {
            synchronized (PROXYED_SET) {
                if (PROXYED_SET.contains(beanName)) {
                    return bean;
                }
                interceptor = null;
                // tcc代理bean
                // 判断接口上是否有@LocalTCC && 接口方法上有@TwoPhaseBusinessAction 注解
                // 同时将@TwoPhaseBusinessAction注解内容解析成tccResource,注册到缓存中,key值为TwoPhaseBusinessAction#name属性。
                // 以供全局2阶段从缓存中获取tccResource
                if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) {
                    // // 若开启了tcc 幂等、空回滚、防悬挂控制(TwoPhaseBusinessAction#useTCCFence为true),定时清理客户端的tcc_fence_log表记录
                    TCCBeanParserUtils.initTccFenceCleanTask(TCCBeanParserUtils.getRemotingDesc(beanName), applicationContext);
                    //设置tcc代理拦截器
                    interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName));
                    ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
                            (ConfigurationChangeListener)interceptor);
                } else {
                    // 非tcc模式bean
                    Class serviceInterface = SpringProxyUtils.findTargetClass(bean);
                    Class[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);
                    // 查找类和方法上是否有全局事务@GlobalTransactional注解
                    if (!existsAnnotation(new Class[]{serviceInterface})
                        && !existsAnnotation(interfacesIfJdk)) {
                        return bean;
                    }
                    // 设置全局事务代理拦截器。说明@GlobalTransactional和tcc相关注解不能标准在同一类上
                    if (globalTransactionalInterceptor == null) {
                        globalTransactionalInterceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
                        ConfigurationCache.addConfigListener(
                                ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
                                (ConfigurationChangeListener)globalTransactionalInterceptor);
                    }
                    interceptor = globalTransactionalInterceptor;
                }

                LOGGER.info("Bean[{}] with name [{}] would use interceptor [{}]", bean.getClass().getName(), beanName, interceptor.getClass().getName());
                // 如果当前bean不是代理对象,调用父类wrapIfNecessary生产代理对象
                if (!AopUtils.isAopProxy(bean)) {
                    bean = super.wrapIfNecessary(bean, beanName, cacheKey);
                } else {
                    AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean);
                    // 拦截器转换成Advisor
                    Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null));
                    int pos;
                    // 按照拦截器的order值,添加拦截器到合适的位置。主要是为了指定该拦截在 spring本地事务拦截器TransactionInterceptor的前面/后面
                    // 像@GlobalTransactionalInterceptor就指定了必须包裹住spring本地事务
                    for (Advisor avr : advisor) {
                        // Find the position based on the advisor's order, and add to advisors by pos
                        pos = findAddSeataAdvisorPosition(advised, avr);
                        advised.addAdvisor(pos, avr);
                    }
                }
                PROXYED_SET.add(beanName);
                return bean;
            }
        } catch (Exception exx) {
            throw new RuntimeException(exx);
        }
    }

    /**
     * 重写getAdvicesAndAdvisorsForBean,返回设置好的拦截器
     * @param beanClass
     * @param beanName
     * @param customTargetSource
     * @return
     * @throws BeansException
     */
    @Override
    protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource customTargetSource)
            throws BeansException {
        // 全局事务拦截器/tcc拦截器
        return new Object[]{interceptor};
    }

}

通过GlobalTransactionScanner添加了全局事务和tcc分支事务的代理拦截,具体拦截逻辑分别在GlobalTransactionalInterceptor和TccActionInterceptor中。

全局事务处理流程(GlobalTransactionalInterceptor)

当代码执行到标注有@GlobalTransactional注解的类或者方法时,进入GlobalTransactionalInterceptor拦截处理。


public class GlobalTransactionalInterceptor implements ConfigurationChangeListener, MethodInterceptor, SeataInterceptor {
    @Override
    public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
        Class targetClass =
            methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null;
        Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass);
        if (specificMethod != null && !specificMethod.getDeclaringClass().equals(Object.class)) {
            final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod);
            // 查找类||方法上的全局事务注解
            final GlobalTransactional globalTransactionalAnnotation =
                getAnnotation(method, targetClass, GlobalTransactional.class);
            // 代理方法增加@GlobalLock+@Transactional  或者 @GlobalTransaction 防止脏读
            final GlobalLock globalLockAnnotation = getAnnotation(method, targetClass, GlobalLock.class);
            boolean localDisable = disable || (degradeCheck && degradeNum >= degradeCheckAllowTimes);
            if (!localDisable) {
                if (globalTransactionalAnnotation != null || this.aspectTransactional != null) {
                    AspectTransactional transactional;
                    if (globalTransactionalAnnotation != null) {
                        transactional = new AspectTransactional(globalTransactionalAnnotation.timeoutMills(),
                            globalTransactionalAnnotation.name(), globalTransactionalAnnotation.rollbackFor(),
                            globalTransactionalAnnotation.noRollbackForClassName(),
                            globalTransactionalAnnotation.noRollbackFor(),
                            globalTransactionalAnnotation.noRollbackForClassName(),
                            globalTransactionalAnnotation.propagation(),
                            globalTransactionalAnnotation.lockRetryInterval(),
                            globalTransactionalAnnotation.lockRetryTimes());
                    } else {
                        transactional = this.aspectTransactional;
                    }
                    //执行全局事务处理逻辑
                    return handleGlobalTransaction(methodInvocation, transactional);
                } else if (globalLockAnnotation != null) {
                    return handleGlobalLock(methodInvocation, globalLockAnnotation);
                }
            }
        }
        return methodInvocation.proceed();
    }


    Object handleGlobalTransaction(final MethodInvocation methodInvocation,
        final AspectTransactional aspectTransactional) throws Throwable {
        boolean succeed = true;
        try {
            return transactionalTemplate.execute(new TransactionalExecutor() {
                @Override
                public Object execute() throws Throwable {
                    return methodInvocation.proceed();
                }

                public String name() {
                    String name = aspectTransactional.getName();
                    if (!StringUtils.isNullOrEmpty(name)) {
                        return name;
                    }
                    // 默认使用 方法名+参数类型名称
                    return formatMethod(methodInvocation.getMethod());
                }

                /**
                 * 组装事务信息
                 * @return
                 */
                @Override
                public TransactionInfo getTransactionInfo() {
                    // reset the value of timeout
                    int timeout = aspectTransactional.getTimeoutMills();
                    if (timeout <= 0 || timeout == DEFAULT_GLOBAL_TRANSACTION_TIMEOUT) {
                        timeout = defaultGlobalTransactionTimeout;
                    }

                    TransactionInfo transactionInfo = new TransactionInfo();
                    transactionInfo.setTimeOut(timeout);
                    transactionInfo.setName(name());
                    transactionInfo.setPropagation(aspectTransactional.getPropagation());
                    transactionInfo.setLockRetryInterval(aspectTransactional.getLockRetryInterval());
                    transactionInfo.setLockRetryTimes(aspectTransactional.getLockRetryTimes());
                    Set rollbackRules = new LinkedHashSet<>();
                    for (Class rbRule : aspectTransactional.getRollbackFor()) {
                        rollbackRules.add(new RollbackRule(rbRule));
                    }
                    for (String rbRule : aspectTransactional.getRollbackForClassName()) {
                        rollbackRules.add(new RollbackRule(rbRule));
                    }
                    for (Class rbRule : aspectTransactional.getNoRollbackFor()) {
                        rollbackRules.add(new NoRollbackRule(rbRule));
                    }
                    for (String rbRule : aspectTransactional.getNoRollbackForClassName()) {
                        rollbackRules.add(new NoRollbackRule(rbRule));
                    }
                    transactionInfo.setRollbackRules(rollbackRules);
                    return transactionInfo;
                }
            });
        } catch (TransactionalExecutor.ExecutionException e) {
            TransactionalExecutor.Code code = e.getCode();
            switch (code) {
                case RollbackDone:
                    throw e.getOriginalException();
                case BeginFailure:
                    succeed = false;
                    failureHandler.onBeginFailure(e.getTransaction(), e.getCause());
                    throw e.getCause();
                case CommitFailure:
                    succeed = false;
                    failureHandler.onCommitFailure(e.getTransaction(), e.getCause());
                    throw e.getCause();
                case RollbackFailure:
                    failureHandler.onRollbackFailure(e.getTransaction(), e.getOriginalException());
                    throw e.getOriginalException();
                case RollbackRetrying:
                    failureHandler.onRollbackRetrying(e.getTransaction(), e.getOriginalException());
                    throw e.getOriginalException();
                default:
                    throw new ShouldNeverHappenException(String.format("Unknown TransactionalExecutor.Code: %s", code));
            }
        } finally {
            if (degradeCheck) {
                EVENT_BUS.post(new DegradeCheckEvent(succeed));
            }
        }
    }



}

这里使用transactionalTemplate模板封装全局事务的处理,来看看它的execute方法

public Object execute(TransactionalExecutor business) throws Throwable {
	// 1. Get transactionInfo
	TransactionInfo txInfo = business.getTransactionInfo();
	if (txInfo == null) {
		throw new ShouldNeverHappenException("transactionInfo does not exist");
	}
	// 1.1 Get current transaction, if not null, the tx role is 'GlobalTransactionRole.Participant'.
	// 这里会获取线程上下文中的全局事务xid,如果xid不为null,说明此xid是通过rpc传递过来的
	// 那当前应用就是作为此次全局事务的参与方,创建DefaultGlobalTransaction对象。否则,tx为null
	GlobalTransaction tx = GlobalTransactionContext.getCurrent();

	// 1.2 Handle the transaction propagation.
	// 全局事务的传播行为,默认REQUIRED,即继承事务
	Propagation propagation = txInfo.getPropagation();
	SuspendedResourcesHolder suspendedResourcesHolder = null;
	try {
		switch (propagation) {
			case NOT_SUPPORTED:
				// If transaction is existing, suspend it.
				if (existingTransaction(tx)) {
					suspendedResourcesHolder = tx.suspend();
				}
				// Execute without transaction and return.
				return business.execute();
			case REQUIRES_NEW:
				// If transaction is existing, suspend it, and then begin new transaction.
				if (existingTransaction(tx)) {
					suspendedResourcesHolder = tx.suspend();
					tx = GlobalTransactionContext.createNew();
				}
				// Continue and execute with new transaction
				break;
			case SUPPORTS:
				// If transaction is not existing, execute without transaction.
				if (notExistingTransaction(tx)) {
					return business.execute();
				}
				// Continue and execute with new transaction
				break;
			case REQUIRED:
				// If current transaction is existing, execute with current transaction,
				// else continue and execute with new transaction.
				break;
			case NEVER:
				// If transaction is existing, throw exception.
				if (existingTransaction(tx)) {
					throw new TransactionException(
						String.format("Existing transaction found for transaction marked with propagation 'never', xid = %s"
								, tx.getXid()));
				} else {
					// Execute without transaction and return.
					return business.execute();
				}
			case MANDATORY:
				// If transaction is not existing, throw exception.
				if (notExistingTransaction(tx)) {
					throw new TransactionException("No existing transaction found for transaction marked with propagation 'mandatory'");
				}
				// Continue and execute with current transaction.
				break;
			default:
				throw new TransactionException("Not Supported Propagation:" + propagation);
		}

		// 1.3 If null, create new transaction with role 'GlobalTransactionRole.Launcher'.
		if (tx == null) {
			// xid为null,说明是全局事务的发起方。
			tx = GlobalTransactionContext.createNew();
		}

		// set current tx config to holder
		GlobalLockConfig previousConfig = replaceGlobalLockConfig(txInfo);

		try {
			// 2. If the tx role is 'GlobalTransactionRole.Launcher', send the request of beginTransaction to TC,
			//    else do nothing. Of course, the hooks will still be triggered.
			// 如果是全局事务的发起方,通知TC服务端注册一个全局事务
			beginTransaction(txInfo, tx);

			Object rs;
			try {
				// Do Your Business
				// 执行业务方法逻辑
				rs = business.execute();
			} catch (Throwable ex) {
				// 3. The needed business exception to rollback.
				// 全局事务回滚处理
				completeTransactionAfterThrowing(txInfo, tx, ex);
				throw ex;
			}

			// 4. everything is fine, commit.
			// 全局事务提交
			commitTransaction(tx);

			return rs;
		} finally {
			//5. clear
			resumeGlobalLockConfig(previousConfig);
			triggerAfterCompletion();
			cleanUp();
		}
	} finally {
		// If the transaction is suspended, resume it.
		if (suspendedResourcesHolder != null) {
			tx.resume(suspendedResourcesHolder);
		}
	}
}

这里的逻辑十分清晰吗,对于全局事务的发起方,一个分三步

  1. 开启一个全局事务,绑定xid。
  2. 执行业务代码逻辑
  3. 执行全局事务提交/回滚逻辑

全局事务注册


public class DefaultGlobalTransaction implements GlobalTransaction {
    @Override
    public void begin(int timeout, String name) throws TransactionException {
        // 非全局事务的发起方,do nothing
        if (role != GlobalTransactionRole.Launcher) {
            assertXIDNotNull();
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Ignore Begin(): just involved in global transaction [{}]", xid);
            }
            return;
        }
        assertXIDNull();
        // 作为全局事务的发起方,这个时候全局事务还未注册。RooRootContext上下文中的xid理应为null
        String currentXid = RootContext.getXID();
        if (currentXid != null) {
            throw new IllegalStateException("Global transaction already exists," +
                " can't begin a new global transaction, currentXid = " + currentXid);
        }
        // TM向TC注册全局事务,绑定tc返回的全局事务id
        xid = transactionManager.begin(null, null, name, timeout);
        status = GlobalStatus.Begin;
        RootContext.bind(xid);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Begin new global transaction [{}]", xid);
        }
    }

}

这里的TransactionManager是通过spi加载的,一般的框架中都会有自己的spi机制。

public class TransactionManagerHolder {

    private static final Logger LOGGER = LoggerFactory.getLogger(TransactionManagerHolder.class);

    private static class SingletonHolder {

        private static TransactionManager INSTANCE = null;

        static {
            try {
                INSTANCE = EnhancedServiceLoader.load(TransactionManager.class);
                LOGGER.info("TransactionManager Singleton {}", INSTANCE);
            } catch (Throwable anyEx) {
                LOGGER.error("Failed to load TransactionManager Singleton! ", anyEx);
            }
        }
    }

    /**
     * Get transaction manager.
     *
     * @return the transaction manager
     */
    public static TransactionManager get() {
        if (SingletonHolder.INSTANCE == null) {
            throw new ShouldNeverHappenException("TransactionManager is NOT ready!");
        }
        return SingletonHolder.INSTANCE;
    }

    /**
     * Set a TM instance.
     *
     * @param mock commonly used for test mocking
     */
    public static void set(TransactionManager mock) {
        SingletonHolder.INSTANCE = mock;
    }

    private TransactionManagerHolder() {

    }
}

分布式事务框架(seata1.5.0)源码分析-TCC模式_第2张图片

可以看到TransactionManager默认为DefaultTransactionManager

public class DefaultTransactionManager implements TransactionManager {
    @Override
    public String begin(String applicationId, String transactionServiceGroup, String name, int timeout)
        throws TransactionException {
        GlobalBeginRequest request = new GlobalBeginRequest();
        request.setTransactionName(name);
        request.setTimeout(timeout);
        // 请求TC开启全局事务
        GlobalBeginResponse response = (GlobalBeginResponse) syncCall(request);
        if (response.getResultCode() == ResultCode.Failed) {
            throw new TmTransactionException(TransactionExceptionCode.BeginFailed, response.getMsg());
        }
        return response.getXid();
    }
    private AbstractTransactionResponse syncCall(AbstractTransactionRequest request) throws TransactionException {
        try {
            // TM通过netty发送全局事务开始请求给TC
            return (AbstractTransactionResponse) TmNettyRemotingClient.getInstance().sendSyncRequest(request);
        } catch (TimeoutException toe) {
            throw new TmTransactionException(TransactionExceptionCode.IO, "RPC timeout", toe);
        }
    }
}

全局事务提交

public class DefaultGlobalTransaction implements GlobalTransaction {

    @SuppressWarnings("lgtm[java/constant-comparison]")
    @Override
    public void commit() throws TransactionException {
        // 全局事务的参与方,do nothing
        if (role == GlobalTransactionRole.Participant) {
            // Participant has no responsibility of committing
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Ignore Commit(): just involved in global transaction [{}]", xid);
            }
            return;
        }
        assertXIDNotNull();
        // 重试提交次数,默认5
        int retry = COMMIT_RETRY_COUNT <= 0 ? DEFAULT_TM_COMMIT_RETRY_COUNT : COMMIT_RETRY_COUNT;
        try {
            while (retry > 0) {
                try {
                    retry--;
                    // TM向TC发送全局事务提交请求,返回全局事务状态
                    status = transactionManager.commit(xid);
                    break;
                } catch (Throwable ex) {
                    LOGGER.error("Failed to report global commit [{}],Retry Countdown: {}, reason: {}", this.getXid(), retry, ex.getMessage());
                    if (retry == 0) {
                        throw new TransactionException("Failed to report global commit", ex);
                    }
                }
            }
        } finally {
            // xid解绑
            if (xid.equals(RootContext.getXID())) {
                suspend();
            }
        }
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("[{}] commit status: {}", xid, status);
        }
    }


}
public class DefaultTransactionManager implements TransactionManager {
    @Override
    public GlobalStatus commit(String xid) throws TransactionException {
        GlobalCommitRequest globalCommit = new GlobalCommitRequest();
        globalCommit.setXid(xid);
        // 请求TC提交全局事务
        GlobalCommitResponse response = (GlobalCommitResponse) syncCall(globalCommit);
        return response.getGlobalStatus();
    }
    private AbstractTransactionResponse syncCall(AbstractTransactionRequest request) throws TransactionException {
        try {
            // TM通过netty发送全局事务请求给TC
            return (AbstractTransactionResponse) TmNettyRemotingClient.getInstance().sendSyncRequest(request);
        } catch (TimeoutException toe) {
            throw new TmTransactionException(TransactionExceptionCode.IO, "RPC timeout", toe);
        }
    }
}

全局事务回滚

public class DefaultGlobalTransaction implements GlobalTransaction {
    @SuppressWarnings("lgtm[java/constant-comparison]")
    @Override
    public void rollback() throws TransactionException {
        // 全局事务的参与方,do nothing
        if (role == GlobalTransactionRole.Participant) {
            // Participant has no responsibility of rollback
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Ignore Rollback(): just involved in global transaction [{}]", xid);
            }
            return;
        }
        assertXIDNotNull();
        // 重试回滚次数,默认5
        int retry = ROLLBACK_RETRY_COUNT <= 0 ? DEFAULT_TM_ROLLBACK_RETRY_COUNT : ROLLBACK_RETRY_COUNT;
        try {
            while (retry > 0) {
                try {
                    retry--;
                    // TM向TC发送全局事务回滚请求,返回全局事务状态
                    status = transactionManager.rollback(xid);
                    break;
                } catch (Throwable ex) {
                    LOGGER.error("Failed to report global rollback [{}],Retry Countdown: {}, reason: {}", this.getXid(), retry, ex.getMessage());
                    if (retry == 0) {
                        throw new TransactionException("Failed to report global rollback", ex);
                    }
                }
            }
        } finally {
            // 解绑xid
            if (xid.equals(RootContext.getXID())) {
                suspend();
            }
        }
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("[{}] rollback status: {}", xid, status);
        }
    }
}
public class DefaultTransactionManager implements TransactionManager {
    @Override
    public GlobalStatus rollback(String xid) throws TransactionException {
        GlobalRollbackRequest globalRollback = new GlobalRollbackRequest();
        globalRollback.setXid(xid);
        // 请求TC回滚全局事务
        GlobalRollbackResponse response = (GlobalRollbackResponse) syncCall(globalRollback);
        return response.getGlobalStatus();
    }
    private AbstractTransactionResponse syncCall(AbstractTransactionRequest request) throws TransactionException {
        try {
            // TM通过netty发送全局事务请求给TC
            return (AbstractTransactionResponse) TmNettyRemotingClient.getInstance().sendSyncRequest(request);
        } catch (TimeoutException toe) {
            throw new TmTransactionException(TransactionExceptionCode.IO, "RPC timeout", toe);
        }
    }
}

一阶段分支事务Try处理流程(TccActionInterceptor)

再来看看业务代码中tcc分支事务的流程,即TccActionInterceptor拦截处理

public class TccActionInterceptor implements MethodInterceptor, ConfigurationChangeListener, Ordered {
    @Override
    public Object invoke(final MethodInvocation invocation) throws Throwable {
        // 不处于全局事务||禁用||saga模式,直接执行业务逻辑
        if (!RootContext.inGlobalTransaction() || disable || RootContext.inSagaBranch()) {
            //not in transaction, or this interceptor is disabled
            return invocation.proceed();
        }
        // 获取方法上的TwoPhaseBusinessAction注解
        Method method = getActionInterfaceMethod(invocation);
        TwoPhaseBusinessAction businessAction = method.getAnnotation(TwoPhaseBusinessAction.class);
        //try method
        // 有businessAction注解执行TCC切面逻辑
        if (businessAction != null) {
            //save the xid
            String xid = RootContext.getXID();
            //save the previous branchType
            BranchType previousBranchType = RootContext.getBranchType();
            //if not TCC, bind TCC branchType
            //绑定tcc分支类型
            if (BranchType.TCC != previousBranchType) {
                RootContext.bindBranchType(BranchType.TCC);
            }
            try {
                //Handler the TCC Aspect, and return the business result
                // 重点在这里,ActionInterceptorHandler处理
                return actionInterceptorHandler.proceed(method, invocation.getArguments(), xid, businessAction,
                        invocation::proceed);
            } finally {
                //if not TCC, unbind branchType
                if (BranchType.TCC != previousBranchType) {
                    RootContext.unbindBranchType();
                }
                //MDC remove branchId
                MDC.remove(RootContext.MDC_KEY_BRANCH_ID);
            }
        }

        //not TCC try method
        return invocation.proceed();
    }
}

分支注册&&上下文参数处理

public class ActionInterceptorHandler {
    public Object proceed(Method method, Object[] arguments, String xid, TwoPhaseBusinessAction businessAction,
                                       Callback targetCallback) throws Throwable {
        //Get action context from arguments, or create a new one and then reset to arguments
        //如果方法入参中含有BusinessActionContext类型的参数,则直接获取方法传入的BusinessActionContext,否则,new BusinessActionContext()
        //BusinessActionContext用来保存全局事务2阶段需要的数据,也就是说可以自己指定传入一个BusinessActionContext,没有也会创建一个
        BusinessActionContext actionContext = getOrCreateActionContextAndResetToArguments(method.getParameterTypes(), arguments);

        //Set the xid
        actionContext.setXid(xid);
        //Set the action name
        String actionName = businessAction.name();
        actionContext.setActionName(actionName);
        //Set the delay report
        actionContext.setDelayReport(businessAction.isDelayReport());

        //Creating Branch Record
        //组装@BusinessActionContextParameter参数注解中的内容到BusinessActionContext中
        //向TC注册tcc分支事务,BusinessActionContext以json格式传到TC服务端,获取TC返回的分支id
        String branchId = doTccActionLogStore(method, arguments, businessAction, actionContext);
        actionContext.setBranchId(branchId);
        //MDC put branchId
        MDC.put(RootContext.MDC_KEY_BRANCH_ID, branchId);

        // save the previous action context
        // BusinessActionContextUtil这个工具,用于获取/设置分支上下文中的业务参数。
        BusinessActionContext previousActionContext = BusinessActionContextUtil.getContext();
        try {
            //share actionContext implicitly
            //这里设置BusinessActionContext到线程上下文中后(分支内有效),我们可以在业务代码中调用BusinessActionContextUtil#addContext添加参数,供tcc2阶段使用
            BusinessActionContextUtil.setContext(actionContext);
            //判断是否开启Tcc 幂等、空回滚、防悬挂控制。建议开启,否则需要自己业务代码做响应的处理
            if (businessAction.useTCCFence()) {
                try {
                    // Use TCC Fence, and return the business result
                    // 重点还要看一下这里的处理,TCC 幂等、空回滚、防悬挂控制
                    return TCCFenceHandler.prepareFence(xid, Long.valueOf(branchId), actionName, targetCallback);
                } catch (SkipCallbackWrapperException | UndeclaredThrowableException e) {
                    Throwable originException = e.getCause();
                    if (originException instanceof FrameworkException) {
                        LOGGER.error("[{}] prepare TCC fence error: {}", xid, originException.getMessage());
                    }
                    throw originException;
                }
            } else {
                //Execute business, and return the business result
                //执行业务逻辑处理
                return targetCallback.execute();
            }
        } finally {
            try {
                //to report business action context finally if the actionContext.getUpdated() is true
                //这里的目前是请求TC,更新server端的BusinessActionContext内容。前面注册TCC分支的时候BusinessActionContext就以json字符串格式传递到了server端
                //tcc 而在try方法中业务逻辑中,我们可以添加参数到BusinessActionContext中。所以需要更新
                BusinessActionContextUtil.reportContext(actionContext);
            } finally {
                if (previousActionContext != null) {
                    // 恢复前一个分支上下文,主要是考虑到分支嵌套,上下文传播问题
                    // recovery the previous action context
                    BusinessActionContextUtil.setContext(previousActionContext);
                } else {
                    // clear the action context
                    BusinessActionContextUtil.clear();
                }
            }
        }
    }
    protected String doTccActionLogStore(Method method, Object[] arguments, TwoPhaseBusinessAction businessAction,
                                         BusinessActionContext actionContext) {
        String actionName = actionContext.getActionName();
        String xid = actionContext.getXid();

        //region fetch context and init action context
        //解析方法入参的BusinessActionContextParameter注解,以key-value形式存储到map中
        Map context = fetchActionRequestContext(method, arguments);
        context.put(Constants.ACTION_START_TIME, System.currentTimeMillis());

        //Init business context
        //组装businessAction、方法名到context中
        initBusinessContext(context, method, businessAction);
        //Init running environment context
        initFrameworkContext(context);
        //将context中的内容合并到BusinessActionContext中
        Map originContext = actionContext.getActionContext();
        if (CollectionUtils.isNotEmpty(originContext)) {
            //Merge context and origin context if it exists.
            //@since: above 1.4.2
            originContext.putAll(context);
            context = originContext;
        } else {
            actionContext.setActionContext(context);
        }

        //endregion

        //Init applicationData
        //最后将BusinessActionContext内容转换成json字符串,key值为actionContext
        Map applicationContext = Collections.singletonMap(Constants.TCC_ACTION_CONTEXT, context);
        String applicationContextStr = JSON.toJSONString(applicationContext);
        try {
            //registry branch record
            //在这里RM向TC注册TCC分支事务,获取TC返回的分支id
            Long branchId = DefaultResourceManager.get().branchRegister(BranchType.TCC, actionName, null, xid,
                    applicationContextStr, null);
            return String.valueOf(branchId);
        } catch (Throwable t) {
            String msg = String.format("TCC branch Register error, xid: %s", xid);
            LOGGER.error(msg, t);
            throw new FrameworkException(t, msg);
        }
    }
}
 
  

在分支注册的时候,会将分支上下文中供TCC2阶段使用的业务参数序列化成json字符串一同发送到TC服务端,执行完业务方法后,还需要再请求一次TC更新业务参数,也是整个json字符串传递过去的。而在我们真实的业务场景中,要传递给2阶段执行的参数可能是非常多的,一旦事务分支较多,这会给网络IO带来很大的开销。所以其实这里我们是可以优化的,上下文中的参数信息完全可以保存到客户端的数据库中(存储在tcc_fence_log中就挺合适的),一次数据库操作就可以完整的保存下来。而发送给TC的数据只需要包含基本的分支信息就可以了,也不用再多请求一次TC来更新参数内容。

TCC模式通过客户端的tcc_fence_log表来完成幂等、空回滚、防悬挂的控制。下面就具体来看下TCCFenceHandler的逻辑

tcc_fence_log

tcc_fence_log表结构

CREATE TABLE IF NOT EXISTS `tcc_fence_log`
(
    `xid`           VARCHAR(128)  NOT NULL COMMENT 'global id',
    `branch_id`     BIGINT        NOT NULL COMMENT 'branch id',
    `action_name`   VARCHAR(64)   NOT NULL COMMENT 'action name',
    `status`        TINYINT       NOT NULL COMMENT 'status(tried:1;committed:2;rollbacked:3;suspended:4)',
    `gmt_create`    DATETIME(3)   NOT NULL COMMENT 'create time',
    `gmt_modified`  DATETIME(3)   NOT NULL COMMENT 'update time',
    PRIMARY KEY (`xid`, `branch_id`),
    KEY `idx_gmt_modified` (`gmt_modified`),
    KEY `idx_status` (`status`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;

可以看到这个表的核心字段就三个:xid-全局事务id,branch_id-分支事务id,action_name-分支名称,主键为(xid,branch_id)

Try阶段插入tcc_fence_log表

public class TCCFenceHandler {
    public static Object prepareFence(String xid, Long branchId, String actionName, Callback targetCallback) {
        // 这里使用spring的TransactionTemplate手工开启事务以确保业务代码和tcc_fence_log表insert处于同一事务中
        return transactionTemplate.execute(status -> {
            try {
                //从数据源获取连接
                Connection conn = DataSourceUtils.getConnection(dataSource);
                //insert记录到tcc_fence_log表中
                boolean result = insertTCCFenceLog(conn, xid, branchId, actionName, TCCFenceConstant.STATUS_TRIED);
                LOGGER.info("TCC fence prepare result: {}. xid: {}, branchId: {}", result, xid, branchId);
                if (result) {
                    //执行业务代码处理
                    return targetCallback.execute();
                } else {
                    throw new TCCFenceException(String.format("Insert tcc fence record error, prepare fence failed. xid= %s, branchId= %s", xid, branchId),
                            FrameworkErrorCode.InsertRecordError);
                }
            } catch (TCCFenceException e) {
                //这里如果抛出duplikey异常,说明已经执行或者正在执行2阶段的cancel。
                //抛出异常以支持空回滚和悬挂
                if (e.getErrcode() == FrameworkErrorCode.DuplicateKeyException) {
                    LOGGER.error("Branch transaction has already rollbacked before,prepare fence failed. xid= {},branchId = {}", xid, branchId);
                    //定时清理掉这种记录
                    addToLogCleanQueue(xid, branchId);
                }
                status.setRollbackOnly();
                throw new SkipCallbackWrapperException(e);
            } catch (Throwable t) {
                status.setRollbackOnly();
                throw new SkipCallbackWrapperException(t);
            }
        });
    }
}
 
  

对于try阶段而已,只需要检查第二阶段是否已经执行完成,如果已完成,则不再执行。这里通过往tcc_fence_log表中insert一条记录来保证此机制,如果出现duplikey异常,说明2阶段cancel比try先执行,2阶段cancel为了支持空回滚,也会往此表中insert一条记录,而2阶段commit则是需要检查表中是否有记录,并更新状态。

二阶段处理流程

首先要明确的是二阶段无论是commit还是rollback都是由TC服务端发起的请求,对于client端来说,一阶段事务结束,业务流程就已经结束了。2阶段处理已经处于处理tc请求的异步线程中了,而非业务线程中。分支提交/回滚请求是由RM来处理的,委托给DefaultRMHandler处理

public class DefaultRMHandler extends AbstractRMHandler {
    //通过spi加载所有的RMHandler放到map中,key值为BranchType,value为具体handler对象
    protected void initRMHandlers() {
        List allRMHandlers = EnhancedServiceLoader.loadAll(AbstractRMHandler.class);
        if (CollectionUtils.isNotEmpty(allRMHandlers)) {
            for (AbstractRMHandler rmHandler : allRMHandlers) {
                allRMHandlersMap.put(rmHandler.getBranchType(), rmHandler);
            }
        }
    }
    //处理分支commit请求,对应BranchCommitRequest
    //返回分支处理结果
    @Override
    public BranchCommitResponse handle(BranchCommitRequest request) {
        MDC.put(RootContext.MDC_KEY_XID, request.getXid());
        MDC.put(RootContext.MDC_KEY_BRANCH_ID, String.valueOf(request.getBranchId()));
		//调用具体的handler处理请求
        return getRMHandler(request.getBranchType()).handle(request);
    }
    //处理分支rollback请求,对应BranchRollbackRequest
    //返回分支处理结果
    @Override
    public BranchRollbackResponse handle(BranchRollbackRequest request) {
        MDC.put(RootContext.MDC_KEY_XID, request.getXid());
        MDC.put(RootContext.MDC_KEY_BRANCH_ID, String.valueOf(request.getBranchId()));
		//调用具体的handler处理请求
        return getRMHandler(request.getBranchType()).handle(request);
    }
    // 根据分支类型获取对应的Rmhandler
    protected AbstractRMHandler getRMHandler(BranchType branchType) {
        return allRMHandlersMap.get(branchType);
    }

}

不同的分支类型对应不同的RmHandler

分布式事务框架(seata1.5.0)源码分析-TCC模式_第3张图片

public abstract class AbstractRMHandler extends AbstractExceptionHandler
    implements RMInboundHandler, TransactionMessageHandler {
    @Override
    public BranchCommitResponse handle(BranchCommitRequest request) {
        //分支提交响应体
        BranchCommitResponse response = new BranchCommitResponse();
        //模板方法执行正常/异常情况响应赋值
        exceptionHandleTemplate(new AbstractCallback() {
            @Override
            public void execute(BranchCommitRequest request, BranchCommitResponse response)
                throws TransactionException {
                //分支提交
                doBranchCommit(request, response);
            }
        }, request, response);
        return response;
    }

    @Override
    public BranchRollbackResponse handle(BranchRollbackRequest request) {
        //分支回滚响应体
        BranchRollbackResponse response = new BranchRollbackResponse();
        //模板方法执行正常/异常情况响应赋值
        exceptionHandleTemplate(new AbstractCallback() {
            @Override
            public void execute(BranchRollbackRequest request, BranchRollbackResponse response)
                throws TransactionException {
                //分支回滚
                doBranchRollback(request, response);
            }
        }, request, response);
        return response;
    }

    protected void doBranchCommit(BranchCommitRequest request, BranchCommitResponse response)
        throws TransactionException {
        String xid = request.getXid();
        long branchId = request.getBranchId();
        String resourceId = request.getResourceId();
        //一阶段初始化好的上下文参数内容
        String applicationData = request.getApplicationData();
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Branch committing: " + xid + " " + branchId + " " + resourceId + " " + applicationData);
        }
        //委托给ResourceManager
        BranchStatus status = getResourceManager().branchCommit(request.getBranchType(), xid, branchId, resourceId,
            applicationData);
        response.setXid(xid);
        response.setBranchId(branchId);
        response.setBranchStatus(status);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Branch commit result: " + status);
        }

    }

    protected void doBranchRollback(BranchRollbackRequest request, BranchRollbackResponse response)
        throws TransactionException {
        String xid = request.getXid();
        long branchId = request.getBranchId();
        String resourceId = request.getResourceId();
        //一阶段初始化好的上下文参数内容
        String applicationData = request.getApplicationData();
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Branch Rollbacking: " + xid + " " + branchId + " " + resourceId);
        }
        //委托给ResourceManager
        BranchStatus status = getResourceManager().branchRollback(request.getBranchType(), xid, branchId, resourceId,
            applicationData);
        response.setXid(xid);
        response.setBranchId(branchId);
        response.setBranchStatus(status);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Branch Rollbacked result: " + status);
        }
    }


}

ResourceManager也是通过spi加载的,委托给DefaultResourceManager,最终定位到TCCResourceManager

Commit处理流程

public class TCCResourceManager extends AbstractResourceManager {

    @Override
    public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId,
                                     String applicationData) throws TransactionException {
        //通过resource名称获取到具体的TCCResource对象,
        //在客户端GlobalTransactionScanner扫描的时候就将各分支方法的@TwoPhaseBusinessAction解析成TCCResource注册到map中
        TCCResource tccResource = (TCCResource)tccResourceCache.get(resourceId);
        if (tccResource == null) {
            throw new ShouldNeverHappenException(String.format("TCC resource is not exist, resourceId: %s", resourceId));
        }
        //目标对象
        Object targetTCCBean = tccResource.getTargetBean();
        //指定的commit方法
        Method commitMethod = tccResource.getCommitMethod();
        if (targetTCCBean == null || commitMethod == null) {
            throw new ShouldNeverHappenException(String.format("TCC resource is not available, resourceId: %s", resourceId));
        }
        try {
            //BusinessActionContext
            //将TC server端返回的applicationData、xid等组装成BusinessActionContext对象,供2阶段commit方法使用
            BusinessActionContext businessActionContext = getBusinessActionContext(xid, branchId, resourceId,
                applicationData);
            //支持在try方法上的@TwoPhaseBusinessAction中commitArgsClasses + commit方法参数注解@BusinessActionContextParameter  自定义2阶段commmit方法入参
            //以@BusinessActionContextParameter的value属性作为key,从businessActionContext中获取到json string,再反序列为commitArgsClasses指定类型对象
            Object[] args = this.getTwoPhaseCommitArgs(tccResource, businessActionContext);
            Object ret;
            boolean result;
            // add idempotent and anti hanging
            // 幂等、空回滚、防悬挂处理
            if (Boolean.TRUE.equals(businessActionContext.getActionContext(Constants.USE_TCC_FENCE))) {
                try {
                    result = TCCFenceHandler.commitFence(commitMethod, targetTCCBean, xid, branchId, args);
                } catch (SkipCallbackWrapperException | UndeclaredThrowableException e) {
                    throw e.getCause();
                }
            } else {
                //直接执行commit方法
                ret = commitMethod.invoke(targetTCCBean, args);
                if (ret != null) {
                    if (ret instanceof TwoPhaseResult) {
                        result = ((TwoPhaseResult)ret).isSuccess();
                    } else {
                        result = (boolean)ret;
                    }
                } else {
                    result = true;
                }
            }
            LOGGER.info("TCC resource commit result : {}, xid: {}, branchId: {}, resourceId: {}", result, xid, branchId, resourceId);
            return result ? BranchStatus.PhaseTwo_Committed : BranchStatus.PhaseTwo_CommitFailed_Retryable;
        } catch (Throwable t) {
            String msg = String.format("commit TCC resource error, resourceId: %s, xid: %s.", resourceId, xid);
            LOGGER.error(msg, t);
            return BranchStatus.PhaseTwo_CommitFailed_Retryable;
        }
    }
}
TCCFenceHandler.commitFence
public class TCCFenceHandler {
    public static boolean commitFence(Method commitMethod, Object targetTCCBean,
                                      String xid, Long branchId, Object[] args) {
        //开启事务保证tcc_fence_log的操作和业务事务绑定再一起
        return transactionTemplate.execute(status -> {
            try {
                Connection conn = DataSourceUtils.getConnection(dataSource);
                //commit操作前,先查询一下有没有该分支一阶段执行记录
                TCCFenceDO tccFenceDO = TCC_FENCE_DAO.queryTCCFenceDO(conn, xid, branchId);
                //没有记录,说明一阶段都未执行,不能执行commit,直接报错。报错后,等待重试
                if (tccFenceDO == null) {
                    throw new TCCFenceException(String.format("TCC fence record not exists, commit fence method failed. xid= %s, branchId= %s", xid, branchId),
                            FrameworkErrorCode.RecordAlreadyExists);
                }
                //幂等控制:分支状态已提交,说明是重试执行,直接返回成功
                if (TCCFenceConstant.STATUS_COMMITTED == tccFenceDO.getStatus()) {
                    LOGGER.info("Branch transaction has already committed before. idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus());
                    return true;
                }
                //理论上讲,不会存在这种情况。会重试
                if (TCCFenceConstant.STATUS_ROLLBACKED == tccFenceDO.getStatus() || TCCFenceConstant.STATUS_SUSPENDED == tccFenceDO.getStatus()) {
                    if (LOGGER.isWarnEnabled()) {
                        LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus());
                    }
                    return false;
                }
                //分支记录存在且还是初始化状态,正常执行commmit中业务逻辑,更新分支记录的状态为COMMITTED
                return updateStatusAndInvokeTargetMethod(conn, commitMethod, targetTCCBean, xid, branchId, TCCFenceConstant.STATUS_COMMITTED, status, args);
            } catch (Throwable t) {
                status.setRollbackOnly();
                throw new SkipCallbackWrapperException(t);
            }
        });
    }
}

Rollback处理流程

public class TCCResourceManager extends AbstractResourceManager {
    @Override
    public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId,
                                       String applicationData) throws TransactionException {
        //通过resource名称获取到具体的TCCResource对象,
        //在客户端GlobalTransactionScanner扫描的时候就将各分支方法的@TwoPhaseBusinessAction解析成TCCResource注册到map中
        TCCResource tccResource = (TCCResource)tccResourceCache.get(resourceId);
        if (tccResource == null) {
            throw new ShouldNeverHappenException(String.format("TCC resource is not exist, resourceId: %s", resourceId));
        }
        //目标对象
        Object targetTCCBean = tccResource.getTargetBean();
        //指定的rollback方法
        Method rollbackMethod = tccResource.getRollbackMethod();
        if (targetTCCBean == null || rollbackMethod == null) {
            throw new ShouldNeverHappenException(String.format("TCC resource is not available, resourceId: %s", resourceId));
        }
        try {
            //BusinessActionContext
            //将TC server端返回的applicationData、xid等组装成BusinessActionContext对象,供2阶段rollback方法使用
            BusinessActionContext businessActionContext = getBusinessActionContext(xid, branchId, resourceId,
                applicationData);
            //支持在try方法上的@TwoPhaseBusinessAction中rollbackArgsClasses + rollback方法参数注解的@BusinessActionContextParameter  自定义2阶段rollback方法入参
            //以@BusinessActionContextParameter的value属性作为key,从businessActionContext中获取到json string,再反序列为rollbackArgsClasses指定类型对象
            Object[] args = this.getTwoPhaseRollbackArgs(tccResource, businessActionContext);
            Object ret;
            boolean result;
            // add idempotent and anti hanging
            // 幂等、空回滚、防悬挂处理
            if (Boolean.TRUE.equals(businessActionContext.getActionContext(Constants.USE_TCC_FENCE))) {
                try {
                    result = TCCFenceHandler.rollbackFence(rollbackMethod, targetTCCBean, xid, branchId,
                            args, tccResource.getActionName());
                } catch (SkipCallbackWrapperException | UndeclaredThrowableException e) {
                    throw e.getCause();
                }
            } else {
                //直接执行rollback方法
                ret = rollbackMethod.invoke(targetTCCBean, args);
                if (ret != null) {
                    if (ret instanceof TwoPhaseResult) {
                        result = ((TwoPhaseResult)ret).isSuccess();
                    } else {
                        result = (boolean)ret;
                    }
                } else {
                    result = true;
                }
            }
            LOGGER.info("TCC resource rollback result : {}, xid: {}, branchId: {}, resourceId: {}", result, xid, branchId, resourceId);
            return result ? BranchStatus.PhaseTwo_Rollbacked : BranchStatus.PhaseTwo_RollbackFailed_Retryable;
        } catch (Throwable t) {
            String msg = String.format("rollback TCC resource error, resourceId: %s, xid: %s.", resourceId, xid);
            LOGGER.error(msg, t);
            return BranchStatus.PhaseTwo_RollbackFailed_Retryable;
        }
    }
}
TCCFenceHandler.rollbackFence
public class TCCFenceHandler {
    public static boolean rollbackFence(Method rollbackMethod, Object targetTCCBean,
                                        String xid, Long branchId, Object[] args, String actionName) {
        //开启事务保证tcc_fence_log的操作和业务事务绑定再一起
        return transactionTemplate.execute(status -> {
            try {
                Connection conn = DataSourceUtils.getConnection(dataSource);
                //rollback操作前,先查询一下有没有该分支一阶段执行记录
                TCCFenceDO tccFenceDO = TCC_FENCE_DAO.queryTCCFenceDO(conn, xid, branchId);
                // non_rollback
                // 空回滚&&防悬挂:没有记录,说明一阶段try未执行,或者本地事务回滚。先insert一条分支记录,确保后到的try方法不会再执行
                // 如果insert成功,说明try还没有执行,空回滚成功
                // 如果insert失败,说明try正在执行,抛出异常,等待TC重试即可
                if (tccFenceDO == null) {
                    boolean result = insertTCCFenceLog(conn, xid, branchId, actionName, TCCFenceConstant.STATUS_SUSPENDED);
                    LOGGER.info("Insert tcc fence record result: {}. xid: {}, branchId: {}", result, xid, branchId);
                    if (!result) {
                        throw new TCCFenceException(String.format("Insert tcc fence record error, rollback fence method failed. xid= %s, branchId= %s", xid, branchId),
                                FrameworkErrorCode.InsertRecordError);
                    }
                    return true;
                } else {
                    // 幂等控制:如果已经rollback,直接返回成功
                    if (TCCFenceConstant.STATUS_ROLLBACKED == tccFenceDO.getStatus() || TCCFenceConstant.STATUS_SUSPENDED == tccFenceDO.getStatus()) {
                        LOGGER.info("Branch transaction had already rollbacked before, idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus());
                        return true;
                    }
                    // 理论上不会出现这种情况
                    if (TCCFenceConstant.STATUS_COMMITTED == tccFenceDO.getStatus()) {
                        if (LOGGER.isWarnEnabled()) {
                            LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus());
                        }
                        return false;
                    }
                }
                // 分支记录存在且还是初始化状态,正常执行rollback中业务逻辑,更新分支记录的状态为ROLLBACKED
                return updateStatusAndInvokeTargetMethod(conn, rollbackMethod, targetTCCBean, xid, branchId, TCCFenceConstant.STATUS_ROLLBACKED, status, args);
            } catch (Throwable t) {
                status.setRollbackOnly();
                throw new SkipCallbackWrapperException(t);
            }
        });
    }
}

TCC幂等&&空回滚&&防悬挂控制总结

  1. 一阶段try:执行前insert一条初始分支记录,如果插入成功,说明第二阶段还没有执行,可以继续执行第一阶段。如果插入失败,则说明第二阶段已经执行或正在执行,抛出异常
  2. 二阶段commit:对于commit,一定要保证commit方法在 Try 方法之后执行。
    1. 首先判断分支记录是否存在,如果不存在,直接抛出异常,等待重试
    2. 如果记录存在且状态为已提交,说明是重复提交请求,直接返回成功
    3. 如果记录存在且状态为初始化,执行commit方法逻辑,并更新分支记录状态为已提交
  3. 二阶段rollback:对于rollback,要支持空回滚,防止事务悬挂
    1. 首先判断分支记录是否存在,如果不存在,则认为 Try 方法还没执行,即是空回滚,应该先插入一条事务记录,确保后续的 Try 方法不会再执行。
    2. 如果insert成功,说明try未执行,空回滚成功
    3. 如果insert失败,说明try正在执行,抛出异常,等待TC重试
    4. 如果分支记录存在并且为已回滚状态,说明是重复rollback请求,直接返回成功
    5. 如果分支记录存在并且为初始化状态,执行rollback方法逻辑,并更新分支记录状态为已回滚

你可能感兴趣的:(源码解析,架构,工作原理,云原生,java)