分布式事务框架(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端独立部署。

  • 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<RollbackRule> 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() {

    }
}

可以看到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<Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<Object> 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<AbstractRMHandler> 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

public abstract class AbstractRMHandler extends AbstractExceptionHandler
    implements RMInboundHandler, TransactionMessageHandler {
    @Override
    public BranchCommitResponse handle(BranchCommitRequest request) {
        //分支提交响应体
        BranchCommitResponse response = new BranchCommitResponse();
        //模板方法执行正常/异常情况响应赋值
        exceptionHandleTemplate(new AbstractCallback<BranchCommitRequest, BranchCommitResponse>() {
            @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<BranchRollbackRequest, BranchRollbackResponse>() {
            @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方法逻辑,并更新分支记录状态为已回滚

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于springcloud+springboot+nacos+openFeign的分布式事务组件seata项目源码.zip 介绍 分布式事务组件seata的使用demo,AT模式TCC模式,集成springboot、springcloud(nacos注册中心、openFeign服务调用、Ribbon负载均衡器)、spring jpa,数据库采用mysql demo中使用的相关版本号,具体请看代码。如果搭建个人demo不成功,验证是否是由版本导致,版本稍有变化可能出现相关组件的版本不一致便会出现许多奇怪问题 seata服务端 1.3 Nacos服务端 1.1.4 spring-cloud-alibaba-dependencies 2.1.0.RELEASE springboot 2.1.3.RELEASE springcloud Greenwich.RELEASE 软件架构 软件架构说明 springcloud-common 公共模块 springcloud-order-AT 订单服务 springcloud-product-AT 商品库存服务 springcloud-consumer-AT 消费调用者 springcloud-business-Tcc 工商银行服务 springcloud-merchants-Tcc 招商银行服务 springcloud-Pay-Tcc 消费调用者 AT模式:springcloud-order-AT,springcloud-product-AT,springcloud-consumer-AT为AT模式Dome;模拟场景用户购买商品下单; 调用流程springcloud-consumer-AT调用订单服务创建订单(新增一条数据到订单表);在调用商品库存服务扣减商品库存数量(修改商品库存表商品数量);最后出现异常则统一回滚,负责统一提交; 第一阶段:准备阶段(prepare)协调者通知参与者准备提交订单,参与者开始投票。协调者完成准备工作向协调者回应Yes。 第二阶段:提交(commit)/回滚(rollback)阶段协调者根据参与者的投票结果发起最终的提交指令。如果有参与者没有准备好则发起回滚指令。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值