Seata源码—7.Seata TCC模式的事务处理一

分布式事务(六)Seata TCC模式-TCC模式介绍 文章目录TCC 基本原理第一阶段 Try第二阶段 Confirm第二阶段 Cancel多个事务并发的情况Seata TCC事务模式 项目源码: https://gitee.com/benwang6/seata-samples TCC 基本原理 TCC 与 Seata AT 事务一样都是两阶段事务,它与 AT 事务的主要区别为: TCC 对业务代码侵入严重 每个阶段的数据操作都要自己进行编码来实现,事务框架无法自动处理。 TCC 效率更高 不必对数据加全局锁,允许多个事务同时操作数据。 第一阶 阅读详情

大纲

1.Seata TCC分布式事务案例配置

2.Seata TCC案例服务提供者启动分析

3.@TwoPhaseBusinessAction注解扫描源码

4.Seata TCC案例分布式事务入口分析

5.TCC核心注解扫描与代理创建入口源码

6.TCC动态代理拦截器TccActionInterceptor

7.Action拦截处理器ActionInterceptorHandler

8.Seata TCC分布式事务的注册提交回滚处理源码

1.Seata TCC分布式事务案例配置

(1)位于seata-samples的tcc模块下的Demo工程

(2)Demo工程的配置文件

(3)Demo工程运行说明

(1)位于seata-samples的tcc模块下的Demo工程

dubbo-tcc-sample模块主要演示了TCC模式下分布式事务的提交和回滚。该Demo中一个分布式事务内会有两个TCC事务参与者,这两个TCC事务参与者分别是TccActionOne和TccActionTwo。分布式事务提交则两者均提交,分布式事务回滚则两者均回滚。

这两个TCC事务参与者均是Dubbo远程服务。一个应用作为服务提供方,会实现这两个TCC参与者,并将它们发布成Dubbo服务。另外一个应用作为事务发起方,会订阅Dubbo服务,然后调用编排TCC参与者,执行远程Dubbo服务。

TccActionOne接口定义如下:

public interface TccActionOne {
    @TwoPhaseBusinessAction(name = "DubboTccActionOne", commitMethod = "commit", rollbackMethod = "rollback")
    public boolean prepare(BusinessActionContext actionContext, 
        @BusinessActionContextParameter(paramName = "a") int a);
    public boolean commit(BusinessActionContext actionContext);
    public boolean rollback(BusinessActionContext actionContext);
}

TccActionTwo接口定义如下:

public interface TccActionTwo {
    @TwoPhaseBusinessAction(name = "DubboTccActionTwo", commitMethod = "commit", rollbackMethod = "rollback")
    public boolean prepare(BusinessActionContext actionContext,
        @BusinessActionContextParameter(paramName = "b") String b,
        @BusinessActionContextParameter(paramName = "c", index = 1) List list);
    public boolean commit(BusinessActionContext actionContext);
    public boolean rollback(BusinessActionContext actionContext);
}

(2)Demo工程的配置文件

一.seata-tcc.xml

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
       default-autowire="byName">

    <!-- fescar bean scanner -->
    <bean class="io.seata.spring.annotation.GlobalTransactionScanner">
        <constructor-arg value="tcc-sample"/>
        <constructor-arg value="my_test_tx_group"/>
    </bean>

    <bean id="tccActionOneImpl" class="io.seata.samples.tcc.dubbo.action.impl.TccActionOneImpl"/>
    <bean id="tccActionTwoImpl" class="io.seata.samples.tcc.dubbo.action.impl.TccActionTwoImpl"/>
    <bean id="tccTransactionService" class="io.seata.samples.tcc.dubbo.service.TccTransactionService"/>
</beans>

二.seata-dubbo-provider.xml

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd" default-autowire="byName">
    <dubbo:application name="tcc-sample">
        <dubbo:parameter key="qos.enable" value="false"/>
    </dubbo:application>

    <!--使用 zookeeper 注册中心暴露服务,注意要先开启 zookeeper-->
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    <dubbo:protocol name="dubbo" port="-1"/>
    <dubbo:provider timeout="10000" threads="10" threadpool="fixed" loadbalance="roundrobin"/>

    <!-- 第一个TCC 参与者服务发布 -->
    <dubbo:service interface="io.seata.samples.tcc.dubbo.action.TccActionOne" ref="tccActionOneImpl"/>
    <!-- 第二个TCC 参与者服务发布 -->
    <dubbo:service interface="io.seata.samples.tcc.dubbo.action.TccActionTwo" ref="tccActionTwoImpl"/>
</beans>

三.seata-dubbo-reference.xml

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd" default-autowire="byName">
    <dubbo:application name="tcc-sample-reference">
        <dubbo:parameter key="qos.enable" value="false"/>
    </dubbo:application>

    <!--使用 zookeeper 注册中心暴露服务,注意要先开启 zookeeper-->
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    <dubbo:protocol name="dubbo" port="-1"/>
    <dubbo:provider timeout="10000" threads="10" threadpool="fixed" loadbalance="roundrobin"/>

    <!-- 第一个TCC参与者 服务订阅 -->
    <dubbo:reference id="tccActionOne" interface="io.seata.samples.tcc.dubbo.action.TccActionOne" check="false" lazy="true"/>
    <!-- 第二个TCC参与者 服务订阅 -->
    <dubbo:reference id="tccActionTwo" interface="io.seata.samples.tcc.dubbo.action.TccActionTwo" check="false" lazy="true"/>
</beans>

(3)Demo工程运行指南

一.启动Seata Server

二.启动Dubbo服务应用

运行DubboTccProviderStarter。该应用会发布Dubbo服务,并且实现了两个TCC参与者。

public class TccProviderStarter extends AbstractStarter {
    public static void main(String[] args) throws Exception {
        new TccProviderStarter().start0(args);
    }
    
    @Override
    protected void start0(String[] args) throws Exception {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
            new String[]{"spring/seata-tcc.xml", "spring/seata-dubbo-provider.xml"}
        );
        new ApplicationKeeper().keep();
    }
}

public class TccActionOneImpl implements TccActionOne {
    @Override
    public boolean prepare(BusinessActionContext actionContext, int a) {
        String xid = actionContext.getXid();
        System.out.println("TccActionOne prepare, xid:" + xid + ", a:" + a);
        return true;
    }
    
    @Override
    public boolean commit(BusinessActionContext actionContext) {
        String xid = actionContext.getXid();
        System.out.println("TccActionOne commit, xid:" + xid + ", a:" + actionContext.getActionContext("a"));
        ResultHolder.setActionOneResult(xid, "T");
        return true;
    }
    
    @Override
    public boolean rollback(BusinessActionContext actionContext) {
        String xid = actionContext.getXid();
        System.out.println("TccActionOne rollback, xid:" + xid + ", a:" + actionContext.getActionContext("a"));
        ResultHolder.setActionOneResult(xid, "R");
        return true;
    }
}

public class TccActionTwoImpl implements TccActionTwo {
    @Override
    public boolean prepare(BusinessActionContext actionContext, String b, List list) {
        String xid = actionContext.getXid();
        System.out.println("TccActionTwo prepare, xid:" + xid + ", b:" + b + ", c:" + list.get(1));
        return true;
    }
    
    @Override
    public boolean commit(BusinessActionContext actionContext) {
        String xid = actionContext.getXid();
        System.out.println("TccActionTwo commit, xid:" + xid + ", b:" + actionContext.getActionContext("b") + ", c:" + actionContext.getActionContext("c"));
        ResultHolder.setActionTwoResult(xid, "T");
        return true;
    }
    
    @Override
    public boolean rollback(BusinessActionContext actionContext) {
        String xid = actionContext.getXid();
        System.out.println("TccActionTwo rollback, xid:" + xid + ", b:" + actionContext.getActionContext("b") + ", c:" + actionContext.getActionContext("c"));
        ResultHolder.setActionTwoResult(xid, "R");
        return true;
    }
}

三.启动事务应用

运行TccConsumerStarter。该应用会订阅Dubbo服务,发起分布式事务,调用上述两个TCC参与者,内含TCC事务提交场景和TCC事务回滚场景的演示。

public class TccConsumerStarter extends AbstractStarter {
    static TccTransactionService tccTransactionService = null;
    
    public static void main(String[] args) throws Exception {
        new TccConsumerStarter().start0(args);
    }
    
    @Override
    protected void start0(String[] args) throws Exception {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
            new String[]{"spring/seata-tcc.xml", "spring/seata-dubbo-reference.xml"}
        );
        tccTransactionService = (TccTransactionService) applicationContext.getBean("tccTransactionService");
        //分布式事务提交demo
        transactionCommitDemo();
        //分布式事务回滚demo
        transactionRollbackDemo();
    }
    
    private static void transactionCommitDemo() throws InterruptedException {
        String txId = tccTransactionService.doTransactionCommit();
        System.out.println(txId);
        Assert.isTrue(StringUtils.isNotEmpty(txId), "事务开启失败");
        System.out.println("transaction commit demo finish.");
    }
    
    private static void transactionRollbackDemo() throws InterruptedException {
        Map map = new HashMap(16);
        try {
            tccTransactionService.doTransactionRollback(map);
            Assert.isTrue(false, "分布式事务未回滚");
        } catch (Throwable t) {
            Assert.isTrue(true, "分布式事务异常回滚");
        }
        String txId = (String) map.get("xid");
        System.out.println(txId);
        System.out.println("transaction rollback demo finish.");
    }
}

public class TccTransactionService {
    private TccActionOne tccActionOne;
    private TccActionTwo tccActionTwo;

    //提交分布式事务
    @GlobalTransactional
    public String doTransactionCommit() {
        //第一个TCC事务参与者
        boolean result = tccActionOne.prepare(null, 1);
        if (!result) {
            throw new RuntimeException("TccActionOne failed.");
        }
        List list = new ArrayList();
        list.add("c1");
        list.add("c2");
        //第二个TCC事务参与者
        result = tccActionTwo.prepare(null, "two", list);
        if (!result) {
            throw new RuntimeException("TccActionTwo failed.");
        }
        return RootContext.getXID();
    }
    
    //回滚分布式事务
    @GlobalTransactional
    public String doTransactionRollback(Map map) {
        //第一个TCC事务参与者
        boolean result = tccActionOne.prepare(null, 1);
        if (!result) {
            throw new RuntimeException("TccActionOne failed.");
        }
        List list = new ArrayList();
        list.add("c1");
        list.add("c2");
        //第二个TCC事务参与者
        result = tccActionTwo.prepare(null, "two", list);
        if (!result) {
            throw new RuntimeException("TccActionTwo failed.");
        }
        map.put("xid", RootContext.getXID());
        throw new RuntimeException("transacton rollback");
    }

    public void setTccActionOne(TccActionOne tccActionOne) {
        this.tccActionOne = tccActionOne;
    }
    
    public void setTccActionTwo(TccActionTwo tccActionTwo) {
        this.tccActionTwo = tccActionTwo;
    }
}

2.Seata TCC案例服务提供者启动分析

添加了@TwoPhaseBusinessAction注解的接口发布成Dubbo服务:

3.@TwoPhaseBusinessAction注解扫描源码

(1)全局事务注解扫描器的wrapIfNecessary()方法扫描Spring Bean

(2)TCCBeanParserUtils的isTccAutoProxy()方法判断是否要创建TCC动态代理

(1)全局事务注解扫描器的wrapIfNecessary()方法扫描Spring Bean

全局事务注解扫描器GlobalTransactionScanner会在调用initClient()方法初始化Seata Client客户端后,通过wrapIfNecessary()方法扫描Spring Bean中含有@TwoPhaseBusinessAction注解的方法。

//AbstractAutoProxyCreator:Spring的动态代理自动创建者
//ConfigurationChangeListener:关注配置变更事件的监听器
//InitializingBean:Spring Bean初始化回调
//ApplicationContextAware:用来感知Spring容器
//DisposableBean:支持可抛弃Bean
public class GlobalTransactionScanner extends AbstractAutoProxyCreator
        implements ConfigurationChangeListener, InitializingBean, ApplicationContextAware, DisposableBean {
    ...
    
    //InitializingBean接口的回调方法
    //Spring容器启动和初始化完毕后,会调用如下的afterPropertiesSet()方法进行回调
    @Override
    public void afterPropertiesSet() {
        //是否禁用了全局事务,默认是false
        if (disableGlobalTransaction) {
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("Global transaction is disabled.");
            }
            ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, (ConfigurationChangeListener)this);
            return;
        }
        //通过CAS操作确保initClient()初始化动作仅仅执行一次
        if (initialized.compareAndSet(false, true)) {
            //initClient()方法会对Seata Client进行初始化,比如和Seata Server建立长连接
            //seata-samples的tcc模块的seata-tcc.xml配置文件里都配置了GlobalTransactionScanner这个Bean
            //而GlobalTransactionScanner这个Bean伴随着Spring容器的初始化完毕,都会回调其初始化逻辑initClient()
            initClient();
        }
    }

    //initClient()是核心方法,负责对Seata Client客户端进行初始化
    private void initClient() {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Initializing Global Transaction Clients ... ");
        }
        if (DEFAULT_TX_GROUP_OLD.equals(txServiceGroup)) {
            LOGGER.warn("...", DEFAULT_TX_GROUP_OLD, DEFAULT_TX_GROUP);
        }
        if (StringUtils.isNullOrEmpty(applicationId) || StringUtils.isNullOrEmpty(txServiceGroup)) {
            throw new IllegalArgumentException(String.format("applicationId: %s, txServiceGroup: %s", applicationId, txServiceGroup));
        }

        //对于Seata Client来说,最重要的组件有两个:
        //一个是TM,即Transaction Manager,用来管理全局事务
        //一个是RM,即Resource Manager,用来管理各分支事务的数据源

        //init TM
        //TMClient.init()会对客户端的TM全局事务管理器进行初始化
        TMClient.init(applicationId, txServiceGroup, accessKey, secretKey);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Transaction Manager Client is initialized. applicationId[{}] txServiceGroup[{}]", applicationId, txServiceGroup);
        }

        //init RM
        //RMClient.init()会对客户端的RM分支事务资源管理器进行初始化
        RMClient.init(applicationId, txServiceGroup);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Resource Manager is initialized. applicationId[{}] txServiceGroup[{}]", applicationId, txServiceGroup);
        }

        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Global Transaction Clients are initialized. ");
        }

        //注册Spring容器被销毁时的回调钩子,释放TM和RM两个组件的一些资源
        registerSpringShutdownHook();
    }
    
    //The following will be scanned, and added corresponding interceptor:
    //添加了如下注解的方法会被扫描到,然后方法会添加相应的拦截器进行拦截
    
    //TM:
    //@see io.seata.spring.annotation.GlobalTransactional // TM annotation
    //Corresponding interceptor:
    //@see io.seata.spring.annotation.GlobalTransactionalInterceptor#handleGlobalTransaction(MethodInvocation, AspectTransactional) // TM handler
    
    //GlobalLock:
    //@see io.seata.spring.annotation.GlobalLock // GlobalLock annotation
    //Corresponding interceptor:
    //@see io.seata.spring.annotation.GlobalTransactionalInterceptor# handleGlobalLock(MethodInvocation, GlobalLock)  // GlobalLock handler
    
    //TCC mode:
    //@see io.seata.rm.tcc.api.LocalTCC // TCC annotation on interface
    //@see io.seata.rm.tcc.api.TwoPhaseBusinessAction // TCC annotation on try method
    //@see io.seata.rm.tcc.remoting.RemotingParser // Remote TCC service parser
    //Corresponding interceptor:
    //@see io.seata.spring.tcc.TccActionInterceptor // the interceptor of TCC mode
    
    @Override
    //由于GlobalTransactionScanner继承了Spring提供的AbstractAutoProxyCreator,
    //所以Spring会把Spring Bean传递给GlobalTransactionScanner.wrapIfNecessary()进行判断;
    //让GlobalTransactionScanner来决定是否要根据Bean的Class、或者Bean的方法是否有上述注解,
    //从而决定是否需要针对Bean的Class创建动态代理,来对添加了注解的方法进行拦截;
    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;

                //check TCC proxy
                //判断传递进来的Bean是否是TCC动态代理
                //服务启动时会把所有的Bean传递进来这个wrapIfNecessary(),检查这个Bean是否需要创建TCC动态代理
                if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) {
                    //init tcc fence clean task if enable useTccFence
                    TCCBeanParserUtils.initTccFenceCleanTask(TCCBeanParserUtils.getRemotingDesc(beanName), applicationContext);
                    //TCC interceptor, proxy bean of sofa:reference/dubbo:reference, and LocalTCC
                    interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName));
                    ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, (ConfigurationChangeListener)interceptor);
                } else {
                    //获取目标class的接口
                    Class<?> serviceInterface = SpringProxyUtils.findTargetClass(bean);
                    Class<?>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);

                    //existsAnnotation()方法会判断Bean的Class或Method是否添加了@GlobalTransactional等注解
                    if (!existsAnnotation(new Class[]{serviceInterface}) && !existsAnnotation(interfacesIfJdk)) {
                        return bean;
                    }

                    if (globalTransactionalInterceptor == null) {
                        //构建一个GlobalTransactionalInterceptor,即全局事务注解的拦截器
                        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());
                if (!AopUtils.isAopProxy(bean)) {//如果这个Bean并不是AOP代理
                    //接下来会基于Spring的AbstractAutoProxyCreator创建针对目标Bean接口的动态代理
                    //这样后续调用到目标Bean的方法,就会调用到TccActionInterceptor拦截器
                    bean = super.wrapIfNecessary(bean, beanName, cacheKey);
                } else {
                    AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean);
                    Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null));
                    int pos;
                    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);
        }
    }
    ...
}

(2)TCCBeanParserUtils的isTccAutoProxy()方法判断是否要创建TCC动态代理

public class TCCBeanParserUtils {
    private TCCBeanParserUtils() {
    }

    //is auto proxy TCC bean
    public static boolean isTccAutoProxy(Object bean, String beanName, ApplicationContext applicationContext) {
        boolean isRemotingBean = parserRemotingServiceInfo(bean, beanName);
        //get RemotingBean description
        RemotingDesc remotingDesc = DefaultRemotingParser.get().getRemotingBeanDesc(beanName);
        //is remoting bean
        if (isRemotingBean) {
            if (remotingDesc != null && remotingDesc.getProtocol() == Protocols.IN_JVM) {
                //LocalTCC
                //创建一个local tcc代理
                return isTccProxyTargetBean(remotingDesc);
            } else {
                //sofa:reference / dubbo:reference, factory bean
                return false;
            }
        } else {
            if (remotingDesc == null) {
                //check FactoryBean
                if (isRemotingFactoryBean(bean, beanName, applicationContext)) {
                    remotingDesc = DefaultRemotingParser.get().getRemotingBeanDesc(beanName);
                    return isTccProxyTargetBean(remotingDesc);
                } else {
                    return false;
                }
            } else {
                return isTccProxyTargetBean(remotingDesc);
            }
        }
    }
    ...
    
    //is TCC proxy-bean/target-bean: LocalTCC , the proxy bean of sofa:reference/dubbo:reference
    public static boolean isTccProxyTargetBean(RemotingDesc remotingDesc) {
        if (remotingDesc == null) {
            return false;
        }
        //check if it is TCC bean
        boolean isTccClazz = false;
        //针对我们的class拿到一个接口class
        Class<?> tccInterfaceClazz = remotingDesc.getInterfaceClass();
        //获取我们的接口里定义的所有的方法
        Method[] methods = tccInterfaceClazz.getMethods();

        TwoPhaseBusinessAction twoPhaseBusinessAction;

        //遍历所有的方法
        for (Method method : methods) {
            //获取的方法是否加了@TwoPhaseBusinessAction注解
            twoPhaseBusinessAction = method.getAnnotation(TwoPhaseBusinessAction.class);
            if (twoPhaseBusinessAction != null) {
                isTccClazz = true;
                break;
            }
        }
        if (!isTccClazz) {
            return false;
        }
        short protocols = remotingDesc.getProtocol();
        //LocalTCC
        if (Protocols.IN_JVM == protocols) {
            //in jvm TCC bean , AOP
            return true;
        }
        //sofa:reference /  dubbo:reference, AOP
        return remotingDesc.isReference();
    }
    ...
}

4.Seata TCC案例分布式事务入口分析

TccTransactionService作为分布式事务的入口,其提交事务和回滚事务的接口都会被添加上@GlobalTransactional注解。

所以应用启动时,TccTransactionService的Bean就会被GlobalTransactionScanner扫描,然后其下添加了@GlobalTransactional注解的接口就会被创建动态代理。

在TccTransactionService的提交分布式事务的接口中,会先后调用TccActionOne和TccActionTwo两个Dubbo服务。并且在调用两个Dubbo服务时,会通过ApacheDubboTransactionPropagationFilter传递xid。

public class TccTransactionService {
    private TccActionOne tccActionOne;
    private TccActionTwo tccActionTwo;

    //提交分布式事务
    @GlobalTransactional
    public String doTransactionCommit() {
        //第一个TCC事务参与者
        boolean result = tccActionOne.prepare(null, 1);
        if (!result) {
            throw new RuntimeException("TccActionOne failed.");
        }
        List list = new ArrayList();
        list.add("c1");
        list.add("c2");
        //第二个TCC事务参与者
        result = tccActionTwo.prepare(null, "two", list);
        if (!result) {
            throw new RuntimeException("TccActionTwo failed.");
        }
        return RootContext.getXID();
    }

    //回滚分布式事务
    @GlobalTransactional
    public String doTransactionRollback(Map map) {
        //第一个TCC事务参与者
        boolean result = tccActionOne.prepare(null, 1);
        if (!result) {
            throw new RuntimeException("TccActionOne failed.");
        }
        List list = new ArrayList();
        list.add("c1");
        list.add("c2");
        //第二个TCC事务参与者
        result = tccActionTwo.prepare(null, "two", list);
        if (!result) {
            throw new RuntimeException("TccActionTwo failed.");
        }
        map.put("xid", RootContext.getXID());
        throw new RuntimeException("transacton rollback");
    }

    public void setTccActionOne(TccActionOne tccActionOne) {
        this.tccActionOne = tccActionOne;
    }
    
    public void setTccActionTwo(TccActionTwo tccActionTwo) {
        this.tccActionTwo = tccActionTwo;
    }
}

动手实践Seata四种模式(XA、AT、TCC、SAGA) XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA 规范 描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对 XA 规范 提供了支持。seata的XA模式做了一些调整,但大体相似:RM一阶段的工作:TC二阶段的工作:RM二阶段的工作:修改application.yml文件(每个参与事务的微服务),开启XA模式: 给发起全局事务的入口方法添加@GlobalTransactional注解,本例中是O 阅读详情

相关推荐

Seata源码—7.Seata TCC模式的事务处理

大纲1.Seata TCC分布式事务案例配置2.Seata TCC案例服务提供者启动分析3.@TwoPhaseBusinessAction注解扫描源码4.Seata TCC案例分布式事务入口分析5.TCC核心注解扫描与代理创建入口源码6.TCC动态代理拦截器TccActionInterceptor7.Action拦截处理器ActionInterceptorHandler8.Seata TCC分布式事务的注册提交回滚处理源码1.Seata TCC分布式事务案例配置(1)位于seata-samples的tcc模

Small world 1125

seata客户端启动ClassNotFoundException: io.seata.spring.annotation.datasource.SeataAutoDataSourceProxyCrea

seata客户端启动:ClassNotFoundException: io.seata.spring.annotation.datasource.SeataAutoDataSourceProxyCrea

黑猫城管的专栏 1932

Spring Cloud整合Seata实现TCC分布式事务模式案例(含源码)

1、Spring Cloud整合Seata实现TCC分布式事务模式案例; 2、TCC分布式事务模式介绍 3、Seata Tcc模式结合JPA使用时,cancel() / commit()方法中的update/delete/insert类型的SQL语句未执行问题分析、解决。

健身变秃,coding变强 7971

seata源码解析:AT模式-启动分析

引言 在官方例子中,AT模式启动主要会加载:io.seata.spring.annotation.GlobalTransactionScanner 这个bean,使用AT模式必须在执行业务功能的方法上加上@GlobalTransactional注解,接下来我们来看看seata启动时做了哪些事情。 xml配置bean:dubbo-business.xml <bean class="io.seata.spring.annotation.GlobalTransactionScanner">

xiaoyi5224765的博客 718

分布式事务Seata源码解析13:TCC事务模式实现原理

1、TCC事务模式实现原理; 2、TCC资源TCCResource解析; 3、TCCResource注册到TC的流程; 4、TCC执行上下文BusinessActionContext详解; 5、TCC分支事务执行流程、ActionInterceptorHandler解析; 6、@LocalTCC使用原理、@TwoPhaseBusinessAction使用原理、@BusinessActionContextParameter使用原理; 7、TCC分支事务提交、回滚原理。

健身变秃,coding变强 5849

别再对着数据手册发愁了!用HFSS 2023 R2搞定ESP32-C3板载天线的阻抗匹配

本文详细介绍了如何利用HFSS 2023 R2进行ESP32-C3板载天线的阻抗匹配优化,解决实际PCB设计中常见的性能偏差问题。通过智能模型导入、科学参数扫描和量化优化流程,显著提升天线性能,适用于WIFI和蓝牙应用场景。

weixin_42523543的博客 447

Seata实战-TCC模式分布式事务原理、源码分析

文章目录原理简介Demo回顾源码分析TCC资源注册开启TCC全局事务TCC拦截器-注册分支事务全局事务提交全局事务回滚 原理简介 TCC相关原理以及Seata中TCC例子demo的使用,在之前文章中已经说明,有兴趣的可以看看 https://blog.csdn.net/hosaos/article/details/89136666 本文会介绍下Seata中TCC模式设计思路及原理,源码版本0....

hosaos的博客 1万+

【网格生成】Gmsh快速入门教程 --1.图形化界面使用

初探 Gmsh软件的主界面如下图所示。通过点击菜单“File” -> "Open"可选择打开已经存在的几何模型或网格文件,初次使用可选择tutorial目录下的t1.geo文件。打开后效果如图所示,t1.geo描述的是一个长方体。 接下来依次尝试左侧菜单栏的功能:Geometry(几何)、Mesh(求解器)。Solver模块需依赖外置求解器,不在本文介绍范围内。 Geometry 该菜单下是与几何相关的功能,如新增一个点/面、体,移动一个体等,具体功能很多可以在后续使用中逐渐尝试学习。本文仅介绍几个

DJ_xiaoming的博客 1万+

Java微服务分布式事务框架seata的TCC模式

上一章节介绍微服务分布式项目中,使用的事务框架seata,本章节介绍分布式事务框架seata TCC模式,以及集成到Springboot、微服务框架里。

Java 技术专栏,从入门到精通,熟悉企业级开发 1万+

群晖Git Server权限配置全攻略:如何让普通用户(非admin)也能安全提交代码?

本文详细解析了群晖Git Server权限配置方案,特别针对普通用户(非admin)安全提交代码的需求。通过SSH服务配置、精细化用户权限管理及仓库创建自动化脚本,实现在DS218+等群晖NAS设备上的高效代码协作,同时确保系统安全。

weixin_42528780的博客 463

高效与灵活相结合:Verilog编写的跨时钟域循环缓冲控制器解析

循环缓冲区,也被称作循环队列、环形缓冲区,是一种数据结构,其主要特性是使用一个固定大小的缓冲区作为数据的存储空间,这个缓冲区在逻辑上被视作是首尾相接的。虽然Verilog是一种常用的硬件描述语言,被广泛应用在FPGA和ASIC设计中,但是在描述循环缓冲区时,它并没有提供一种直接且灵活的方式来指定循环缓冲区的参数,如字长、缓冲区深度、缓冲区数量等。这给硬件设计带来了一定的不便。同时,该控制器的设计也充分考虑了硬件资源的利用率,通过优化的算法和数据结构,使得在实现高性能的同时,也能够有效地节省硬件资源。

qq_38334677的博客 1221

Seata源码——TCC模式总结

TCC 是分布式事务中的二阶段提交协议,它的全称为 Try-Confirm-Cancel,即资源预留(Try)、确认操作(Confirm)、取消操作(Cancel)

qq_41956309的博客 1741

11、基于拓扑质心和树编辑距离的RNA二级结构比较方法

本文介绍了一种基于拓扑质心和树编辑距离的RNA二级结构比较方法,特别适用于处理具有假结结构的RNA。通过PEELING算法识别拓扑质心并将其转换为树结构,结合树编辑距离进行高效比较,并提供工具planeGraph2tree实现该方法。博文详细描述了方法原理、操作流程以及应用示例,展示了其在功能性非编码RNA分析中的实用性。

resnet7explorer的博客 111
上一篇: Seata源码—6.Seata AT模式的数据源代理三
下一篇: Seata源码—7.Seata TCC模式的事务处理二
东阳马生架构
博客等级 码龄2年 2727粉丝 187原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值