seata TCC事务流程

版本:1.2.0

TCC是一种资源服务化的两阶段提交协议,用户需要实现Try、Confirm/Cancel接口。通过一阶段的Try操作决定执行Confirm(确认)或者Cancel(取消)操作。

image

Seata TCC三种接口去对资源进行管理

  • prepare资源预留
  • commit资源提交
  • rollback 资源回滚

然后将分支事务纳入到全局事务中,对全局事务管理。

使用

通过与Spring的结合使用了解Seata TCC模式的执行流程。

  • 定义接口,通过实现接口去进行资源的预留提交和回滚操作,每个分支事务都是RM
public interface TccActionOne {

    @TwoPhaseBusinessAction(name="TccActionOne",commitMethod = "commit",rollbackMethod = "rollback")
    public boolean prepare(BusinessActionContext actionContext,int num);
    public boolean commit(BusinessActionContext actionContext);
    public boolean rollback(BusinessActionContext actionContext);
}

每个预留资源接口添加@TwoPhaseBusinessAction(name="TccActionOne",commitMethod = "commit",rollbackMethod = "rollback")注解去定义各接口操作

  • 调用接口,添加@GlobalTransactional将分支事务纳入全局事务
    @GlobalTransactional
    public String doTransactionCommit(){
        //第一个TCC 事务参与者
        boolean result = tccActionOne.prepare(null, 1);
        if(!result){
            throw new RuntimeException("TccActionOne failed.");
        }
        //第二个TCC 事务参与者
        result = tccActionTwo.prepare(null, "two");
        if(!result){
            throw new RuntimeException("TccActionTwo failed.");
        }
        return RootContext.getXID();
    }
  • 注入GlobalTransactionScanner,初始化TMClinet和RmClient及其他模式配置的初始化
    @Bean
    public GlobalTransactionScanner globalTransactionScanner(){
        return new GlobalTransactionScanner("ijcf_tx_group");
    }

初始化

通过GlobalTransactionScanner进行初始化

public class GlobalTransactionScanner extends AbstractAutoProxyCreator
    implements InitializingBean, ApplicationContextAware,
    DisposableBean

image

  • 实现InitializingBeanafterPropertiesSet方法,会在类实例化时执行,主要是用于初始化TM\RM客户端并向Server注册
    public void afterPropertiesSet() {
        initClient();
    }
    
   private void initClient() {

        if (StringUtils.isNullOrEmpty(applicationId) || StringUtils.isNullOrEmpty(txServiceGroup)) {
            throw new IllegalArgumentException(String.format("applicationId: %s, txServiceGroup: %s", applicationId, txServiceGroup));
        }
        //init TM
        TMClient.init(applicationId, txServiceGroup);

        //init RM
        RMClient.init(applicationId, txServiceGroup);

        //注册钩子
        registerSpringShutdownHook();

    }
  • 实现ApplicationContextAwaresetApplicationContext方法获得spring容器
  • 实现DisposableBeandestroy方法,在容器关闭时进行操作
  • 继承AbstractAutoProxyCreator类重写wrapIfNecessary进行切面代理。
    @Override
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        //是否禁用了全局事务
        if (disableGlobalTransaction) {
            return bean;
        }
        try {
            synchronized (PROXYED_SET) {
                //检查当前beanName是否已经处理过 如果处理过本次就不处理
                if (PROXYED_SET.contains(beanName)) {
                    return bean;
                }
                interceptor = null;
                //check TCC proxy
                //校验是否为TCC分支事务类
                //会在这里进行资源的注册
                if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) {
                    //TCC interceptor, proxy bean of sofa:reference/dubbo:reference, and LocalTCC
                    //创建拦截器  
                    interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName));
                } else {
                    Class<?> serviceInterface = SpringProxyUtils.findTargetClass(bean);
                    Class<?>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);
                    //判断类方法中是否添加@GlobalTransactional 或@GlobalLock注解,判断当前是否全局事务类 
                    if (!existsAnnotation(new Class[]{serviceInterface})
                        && !existsAnnotation(interfacesIfJdk)) {
                        return bean;
                    }

                    if (interceptor == null) {
                        //创建全局事务拦截器
                        interceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
                        ConfigurationFactory.getInstance().addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, (ConfigurationChangeListener) interceptor);
                    }
                }

                LOGGER.info("Bean[{}] with name [{}] would use interceptor [{}]", bean.getClass().getName(), beanName, interceptor.getClass().getName());
                if (!AopUtils.isAopProxy(bean)) {
                    //执行父类方法 添加 拦截器
                    bean = super.wrapIfNecessary(bean, beanName, cacheKey);
                } else {
                    AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean);
                    Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null));
                    for (Advisor avr : advisor) {
                        advised.addAdvisor(0, avr);
                    }
                }
                PROXYED_SET.add(beanName);
                return bean;
            }
        } catch (Exception exx) {
            throw new RuntimeException(exx);
        }
    }

通过上面的源码可以看出有两个核心拦截器,TccActionInterceptorTCC模式拦截器和GlobalTransactionalInterceptor全局事务拦截器,这两个拦截器为方法拦截器,会在类调用方法时执行其invoke方法。

TCC流程

调用doTransactionCommit方法开始分布式事务执行。通过上面分析会执行GlobalTransactionalInterceptor全局事务拦截器的invoke方法

  • GlobalTransactionalInterceptor.invoke
    @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);
        final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod);
        
        final GlobalTransactional globalTransactionalAnnotation = getAnnotation(method, GlobalTransactional.class);
        final GlobalLock globalLockAnnotation = getAnnotation(method, GlobalLock.class);
        //GlobalTransactional全局事务注解切面逻辑
        if (!disable && globalTransactionalAnnotation != null) {
            return handleGlobalTransaction(methodInvocation, globalTransactionalAnnotation);
        } else if (!disable && globalLockAnnotation != null) {
        //GlobalLock 注解切面逻辑
            return handleGlobalLock(methodInvocation);
        } else {
            return methodInvocation.proceed();
        }
    }

GlobalLock注解是为了保证事务隔离,并不会加入到全局事务

  • GlobalTransactionalInterceptor.handleGlobalTransaction
    private Object handleGlobalTransaction(final MethodInvocation methodInvocation,
                                           final GlobalTransactional globalTrxAnno) throws Throwable {
        try {
            return transactionalTemplate.execute(new TransactionalExecutor() {
                @Override
                public Object execute() throws Throwable {
                    return methodInvocation.proceed();
                }
                ...
            });
        } catch (TransactionalExecutor.ExecutionException e) {
            ...
        }
    }

通过TransactionalExecutor执行方法,而TransactionalTemplate.execute才是开始事务的关键。

  • transactionalTemplate.execute
    public Object execute(TransactionalExecutor business) throws Throwable {
        ...
        // 1.1 获取全局事务如果不存在则创建
        GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
        ...
        try {
            ...

            try {

                // 2. 开启事务
                beginTransaction(txInfo, tx);

                Object rs = null;
                try {

                    // 2.1执行业务代码
                    rs = business.execute();

                } catch (Throwable ex) {

                    // 3.异常回滚
                    completeTransactionAfterThrowing(txInfo, tx, ex);
                    throw ex;
                }

                // 4. 提交
                commitTransaction(tx);

                return rs;
            } finally {
                //5. 清理执行结束钩子
                triggerAfterCompletion();
                cleanUp();
            }


    }

主要流程:

  • 1.创建全局事务
  • 2.TM向TC发送请求开启全局事务
  • 3.执行业务代码
  • 4.根据执行结果回滚或提交
开启事务

TransactionalTemplate.beginTransaction

    private void beginTransaction(TransactionInfo txInfo, GlobalTransaction tx) throws TransactionalExecutor.ExecutionException {
            ...
            //执行前钩子
            triggerBeforeBegin();
            //开启事务
            tx.begin(txInfo.getTimeOut(), txInfo.getName());
            //执行后钩子
            triggerAfterBegin();
            ...
    }

GlobalTransaction.begin

    public void begin(int timeout, String name) throws TransactionException {
        ...
        // 通过TransactionManager开启事务
        xid = transactionManager.begin(null, null, name, timeout);
        status = GlobalStatus.Begin;
        //绑定到RootContext
        RootContext.bind(xid);
        ...

    }

开启全局事务
TransactionManager.begin:TransactionManager,TM 用于开启和管理全局事务,默认实现DefaultTransactionManager

    @Override
    public String begin(String applicationId, String transactionServiceGroup, String name, int timeout)
        throws TransactionException {
        //创建一个全局事务开启请求
        GlobalBeginRequest request = new GlobalBeginRequest();
        request.setTransactionName(name);
        request.setTimeout(timeout);
        //向服务端发送全局事务开启请求,服务端会返回全局事务ID XID
        GlobalBeginResponse response = (GlobalBeginResponse)syncCall(request);
        if (response.getResultCode() == ResultCode.Failed) {
            throw new TmTransactionException(TransactionExceptionCode.BeginFailed, response.getMsg());
        }
        return response.getXid();
    }

上篇文章介绍了服务端DefaultCore是核心实现,驱动全局事务的开启提交回滚操作。服务端接收请求响应
DefaultCore.begin

    public String begin(String applicationId, String transactionServiceGroup, String name, int timeout)
            throws TransactionException {
        //创建一个全局事务上下文
        GlobalSession session = GlobalSession.createGlobalSession(applicationId, transactionServiceGroup, name,
                timeout);
        session.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
        //开启事务
        session.begin();

        eventBus.post(new GlobalTransactionEvent(session.getTransactionId(), GlobalTransactionEvent.ROLE_TC,
                session.getTransactionName(), session.getBeginTime(), null, session.getStatus()));
        //返回全局事务ID XID
        return session.getXid();
    }
执行业务代码

**
上文介绍在TCC分支会创建拦截器TccActionInterceptor,在执行分支的业务代码时会调用其invoke方法。

    public Object invoke(final MethodInvocation invocation) throws Throwable {
        if (!RootContext.inGlobalTransaction()) {
            //不在事务中
            return invocation.proceed();
        }
        Method method = getActionInterfaceMethod(invocation);
        //获取TwoPhaseBusinessAction注解信息
        TwoPhaseBusinessAction businessAction = method.getAnnotation(TwoPhaseBusinessAction.class);
        //执行try操作
        if (businessAction != null) {
            String xid = RootContext.getXID();
            //清理核心上文
            RootContext.unbind();
            RootContext.bindInterceptorType(xid, BranchType.TCC);
            try {
                Object[] methodArgs = invocation.getArguments();
                //拦截器Handler 进行分支注册及程序执行
                Map<String, Object> ret = actionInterceptorHandler.proceed(method, methodArgs, xid, businessAction,
                        invocation::proceed);
                //返回结果
                return ret.get(Constants.TCC_METHOD_RESULT);
            } finally {
                //恢复核心上下文
                RootContext.unbindInterceptorType();
                RootContext.bind(xid);
            }
        }
        return invocation.proceed();
    }

ActionInterceptorHandler.proceed

    public Map<String, Object> proceed(Method method, Object[] arguments, String xid, TwoPhaseBusinessAction businessAction,
                                       Callback<Object> targetCallback) throws Throwable {
        Map<String, Object> ret = new HashMap<>(4);

        //TCC name
        String actionName = businessAction.name();
        //构造业务上下文
        BusinessActionContext actionContext = new BusinessActionContext();
        actionContext.setXid(xid);
        //设置name
        actionContext.setActionName(actionName);

        //RM分支注册,服务端响应BranchId
        String branchId = doTccActionLogStore(method, arguments, businessAction, actionContext);
        actionContext.setBranchId(branchId);

        //业务上下文BusinessActionContext 添加到方法参数中
        //上文使用中--tccActionOne.prepare(null, 1);参数需要设置为nul
        Class<?>[] types = method.getParameterTypes();
        int argIndex = 0;
        for (Class<?> cls : types) {
            if (cls.getName().equals(BusinessActionContext.class.getName())) {
                arguments[argIndex] = actionContext;
                break;
            }
            argIndex++;
        }
        //返回最终参数
        ret.put(Constants.TCC_METHOD_ARGUMENTS, arguments);
        //执行业务方法
        ret.put(Constants.TCC_METHOD_RESULT, targetCallback.execute());
        return ret;
    }
    
    protected String doTccActionLogStore(Method method, Object[] arguments, TwoPhaseBusinessAction businessAction,
                                         BusinessActionContext actionContext) {
        String actionName = actionContext.getActionName();
        String xid = actionContext.getXid();
        //获取扩展参数 @BusinessActionContextParameter注解参数可以在Context中获取
        Map<String, Object> context = fetchActionRequestContext(method, arguments);
        context.put(Constants.ACTION_START_TIME, System.currentTimeMillis());

        //初始化业务上下文
        initBusinessContext(context, method, businessAction);
        //Init running environment context
        initFrameworkContext(context);
        actionContext.setActionContext(context);

        //应用数据
        Map<String, Object> applicationContext = new HashMap<>(4);
        applicationContext.put(Constants.TCC_ACTION_CONTEXT, context);
        String applicationContextStr = JSON.toJSONString(applicationContext);
        try {
            //RM向TC注册分支,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);
        }
    }

TCC分支事务执行会先执行切面拦截器invoke方法,其实际过程是通过切面处理器ActionInterceptorHandler.proceed方法去完成分支注册方法执行。而分支注册是默认RM实现DefaultResourceManager.branchRegister完成。DefaultResourceManager主要管理各类RM,而分支注册是根据不同模式下的Rm去完成分支注册。TCC模式下通过TCCResourceManager完成分支注册,其内部调用父类AbstractResourceManager.branchRegister最终完成分支注册。

DefaultResourceManager.branchRegister

    public Long branchRegister(BranchType branchType, String resourceId,
                               String clientId, String xid, String applicationData, String lockKeys)
        throws TransactionException {
        //判断分支类型 TCC / SAGA 去完成注册
        return getResourceManager(branchType).branchRegister(branchType, resourceId, clientId, xid, applicationData,
            lockKeys);
    }

AbstractResourceManager.branchRegister

    public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid, String applicationData, String lockKeys) throws TransactionException {
            ...
            BranchRegisterRequest request = new BranchRegisterRequest();
            request.setXid(xid);
            request.setLockKey(lockKeys);
            request.setResourceId(resourceId);
            request.setBranchType(branchType);
            request.setApplicationData(applicationData);
            //向服务端发起分支事务注册 ,服务端响应
            BranchRegisterResponse response = (BranchRegisterResponse) RmRpcClient.getInstance().sendMsgWithResponse(request);
            if (response.getResultCode() == ResultCode.Failed) {
                throw new RmTransactionException(response.getTransactionExceptionCode(), String.format("Response[ %s ]", response.getMsg()));
            }
            return response.getBranchId();
            ...
    }

服务端

    public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid,
                               String applicationData, String lockKeys) throws TransactionException {
        //校验全局Session
        GlobalSession globalSession = assertGlobalSessionNotNull(xid, false);
        return SessionHolder.lockAndExecute(globalSession, () -> {
            globalSessionStatusCheck(globalSession);
            globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
            //创建分支session
            BranchSession branchSession = SessionHelper.newBranchByGlobal(globalSession, branchType, resourceId,
                    applicationData, lockKeys, clientId);
            //锁定当前session
            branchSessionLock(globalSession, branchSession);
            try {
                //把当前分支事务添加到全局事务中
                globalSession.addBranch(branchSession);
            } catch (RuntimeException ex) {
                //解除锁定
                branchSessionUnlock(branchSession);
                ...
            }
            //返回分支ID
            return branchSession.getBranchId();
        });
    }

这里就完成了方法的执行

事务回滚

方法执行出现异常,会向TC服务端发起全局回滚请求,TC 在收到回滚请求后通知各事务分支进行分支回滚并返回TC分支回滚状态,事务回滚的方法与事务提交的逻辑一致

TransactionalTemplate.rollbackTransaction->GlobalTransaction.rollback->DefaultGlobalTrans.rollback->DefaultTransactionManager.rollback向服务器发送全局回滚请求

    public GlobalStatus rollback(String xid) throws TransactionException {
        //构造全局回滚请求
        GlobalRollbackRequest globalRollback = new GlobalRollbackRequest();
        globalRollback.setXid(xid);
        //发送请求 TC响应结果
        GlobalRollbackResponse response = (GlobalRollbackResponse)syncCall(globalRollback);
        return response.getGlobalStatus();
    }

服务器DefaultCore.rollback

    public GlobalStatus rollback(String xid) throws TransactionException {
        GlobalSession globalSession = SessionHolder.findGlobalSession(xid);
        if (globalSession == null) {
            return GlobalStatus.Finished;
        }
        globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
        // 锁定当前状态
        boolean shouldRollBack = SessionHolder.lockAndExecute(globalSession, () -> {
            globalSession.close(); // 关闭全局session注册
            if (globalSession.getStatus() == GlobalStatus.Begin) {
                globalSession.changeStatus(GlobalStatus.Rollbacking);
                return true;
            }
            return false;
        });
        if (!shouldRollBack) {
            return globalSession.getStatus();
        }

        doGlobalRollback(globalSession, false);
        return globalSession.getStatus();
    }
    
    public boolean doGlobalRollback(GlobalSession globalSession, boolean retrying) throws TransactionException {
        boolean success = true;
        // 开启回滚事件
        eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC,
                globalSession.getTransactionName(), globalSession.getBeginTime(), null, globalSession.getStatus()));
        //是否为SAGA 使用SAGA模式回滚
        if (globalSession.isSaga()) {
            success = getCore(BranchType.SAGA).doGlobalRollback(globalSession, retrying);
        } else {
            //回滚各分支事务
            for (BranchSession branchSession : globalSession.getReverseSortedBranches()) {
                BranchStatus currentBranchStatus = branchSession.getStatus();
                //一阶段失败不需要回滚,从全局事务中移除
                if (currentBranchStatus == BranchStatus.PhaseOne_Failed) {
                    globalSession.removeBranch(branchSession);
                    continue;
                }
                try {
                    //分支回滚
                    BranchStatus branchStatus = branchRollback(globalSession, branchSession);
                    ...
                } catch (Exception ex) {
                    ...
                }
            }
            ...
        }
        ...
        return success;
    }

    
    public BranchStatus branchRollback(GlobalSession globalSession, BranchSession branchSession) throws TransactionException {
        try {
            //构造分支回滚请求
            BranchRollbackRequest request = new BranchRollbackRequest();
            request.setXid(branchSession.getXid());
            request.setBranchId(branchSession.getBranchId());
            request.setResourceId(branchSession.getResourceId());
            request.setApplicationData(branchSession.getApplicationData());
            request.setBranchType(branchSession.getBranchType());
            //向客户端TM发送请求回滚分支
            return branchRollbackSend(request, globalSession, branchSession);
        } catch (IOException | TimeoutException e) {
            ...
        }
    }

TCC RM执行分支回滚TCCResourceManager.branchRollback

    public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId,
                                       String applicationData) throws TransactionException {
            ...
            BusinessActionContext businessActionContext = getBusinessActionContext(xid, branchId, resourceId,
                applicationData);
            //通过BusinessActionContext注解配置的rollback方法进行分支回滚
            Object ret = rollbackMethod.invoke(targetTCCBean, businessActionContext);
            LOGGER.info("TCC resource rollback result : {}, xid: {}, branchId: {}, resourceId: {}", ret, xid, branchId, resourceId);
            ...
    }

到这里也就完成了对全局事务与分支事务的回滚操作,事务提交操作与之一致,在分支提交时会调用预留的接口,从而完成分布式事务操作。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值