Seata源码—2.seata-samples项目介绍

大纲

1.seata-samples的配置文件和启动类

2.seata-samples业务服务启动时的核心工作

3.seata-samples库存服务的连接池配置

4.Seata对数据库连接池代理配置的分析

5.Dubbo RPC通信过程中传递全局事务XID

6.Seata跟Dubbo整合的Filter(基于SPI机制)

7.seata-samples的AT事务例子原理流程

8.Seata核心配置文件file.conf的内容介绍

1.seata-samples的配置文件和启动类

(1)seata-samples的测试步骤

(2)seata-samples用户服务的配置和启动类

(3)seata-samples库存服务的配置和启动类

(4)seata-samples订单服务的配置和启动类

(5)seata-samples业务服务的配置和启动类

示例仓库:

复制代码
https://github.com/seata/seata-samples

示例代码的模块ID:seata-samples-dubbo

(1)seata-samples的测试步骤

步骤一:启动DubboAccountServiceStarter

步骤二:启动DubboStorageServiceStarter

步骤三:启动DubboOrderServiceStarter

步骤四:运行DubboBusinessTester

(2)seata-samples用户服务的配置和启动类

dubbo-account-service.xml配置文件:

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    
    <!-- 把jdbc.properties文件里的配置加载进来 -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations" value="classpath:jdbc.properties"/>
    </bean>
    
    <!-- 将配置文件里的值注入到库存服务的数据库连接池accountDataSource中 -->
    <bean name="accountDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.account.url}"/>
        <property name="username" value="${jdbc.account.username}"/>
        <property name="password" value="${jdbc.account.password}"/>
        <property name="driverClassName" value="${jdbc.account.driver}"/>
        <property name="initialSize" value="0"/>
        <property name="maxActive" value="180"/>
        <property name="minIdle" value="0"/>
        <property name="maxWait" value="60000"/>
        <property name="validationQuery" value="Select 'x' from DUAL"/>
        <property name="testOnBorrow" value="false"/>
        <property name="testOnReturn" value="false"/>
        <property name="testWhileIdle" value="true"/>
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
        <property name="minEvictableIdleTimeMillis" value="25200000"/>
        <property name="removeAbandoned" value="true"/>
        <property name="removeAbandonedTimeout" value="1800"/>
        <property name="logAbandoned" value="true"/>
        <property name="filters" value="mergeStat"/>
    </bean>
    
    <!-- 创建数据库连接池代理,通过DataSourceProxy代理accountDataSourceProxy数据库连接池 -->
    <bean id="accountDataSourceProxy" class="io.seata.rm.datasource.DataSourceProxy">
        <constructor-arg ref="accountDataSource"/>
    </bean>
    
    <!-- 将数据库连接池代理accountDataSourceProxy注入到JdbcTemplate数据库操作组件中-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="accountDataSourceProxy"/>
    </bean>

    <dubbo:application name="dubbo-demo-account-service">
        <dubbo:parameter key="qos.enable" value="false"/>
    </dubbo:application>
    <dubbo:registry address="zookeeper://localhost:2181" />
    <dubbo:protocol name="dubbo" port="20881"/>
    <dubbo:service interface="io.seata.samples.dubbo.service.AccountService" ref="service" timeout="10000"/>
    
    <!-- 将JdbcTemplate数据库操作组件注入到AccountServiceImpl中 -->
    <bean id="service" class="io.seata.samples.dubbo.service.impl.AccountServiceImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>
    
    <!-- 全局事务注解扫描组件 -->
    <bean class="io.seata.spring.annotation.GlobalTransactionScanner">
        <constructor-arg value="dubbo-demo-account-service"/>
        <constructor-arg value="my_test_tx_group"/>
    </bean>
</beans>

启动类:

复制代码
public class DubboAccountServiceStarter {
    //Account service is ready. A buyer register an account: U100001 on my e-commerce platform
    public static void main(String[] args) {
        ClassPathXmlApplicationContext accountContext = new ClassPathXmlApplicationContext(
            new String[] {"spring/dubbo-account-service.xml"}
        );
        accountContext.getBean("service");
        JdbcTemplate accountJdbcTemplate = (JdbcTemplate)accountContext.getBean("jdbcTemplate");
        accountJdbcTemplate.update("delete from account_tbl where user_id = 'U100001'");
        accountJdbcTemplate.update("insert into account_tbl(user_id, money) values ('U100001', 999)");
        new ApplicationKeeper(accountContext).keep();
    }
}

//The type Application keeper.
public class ApplicationKeeper {
    private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationKeeper.class);
    private final ReentrantLock LOCK = new ReentrantLock();
    private final Condition STOP = LOCK.newCondition();

    //Instantiates a new Application keeper.
    public ApplicationKeeper(AbstractApplicationContext applicationContext) {
        addShutdownHook(applicationContext);
    }
    
    private void addShutdownHook(final AbstractApplicationContext applicationContext) {
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    applicationContext.close();
                    LOGGER.info("ApplicationContext " + applicationContext + " is closed.");
                } catch (Exception e) {
                    LOGGER.error("Failed to close ApplicationContext", e);
                }

                LOCK.lock();
                try {
                    STOP.signal();
                } finally {
                    LOCK.unlock();
                }
            }
        }));
    }
    
    public void keep() {
        LOCK.lock();
        try {
            LOGGER.info("Application is keep running ... ");
            STOP.await();
        } catch (InterruptedException e) {
            LOGGER.error("ApplicationKeeper.keep() is interrupted by InterruptedException!", e);
        } finally {
            LOCK.unlock();
        }
    }
}

(3)seata-samples库存服务的配置和启动类

dubbo-stock-service.xml配置文件:

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    
    <!-- 把jdbc.properties文件里的配置加载进来 -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations" value="classpath:jdbc.properties"/>
    </bean>
    
    <!-- 将配置文件里的值注入到库存服务的数据库连接池stockDataSource中 -->
    <bean name="stockDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.stock.url}"/>
        <property name="username" value="${jdbc.stock.username}"/>
        <property name="password" value="${jdbc.stock.password}"/>
        <property name="driverClassName" value="${jdbc.stock.driver}"/>
        <property name="initialSize" value="0"/>
        <property name="maxActive" value="180"/>
        <property name="minIdle" value="0"/>
        <property name="maxWait" value="60000"/>
        <property name="validationQuery" value="Select 'x' from DUAL"/>
        <property name="testOnBorrow" value="false"/>
        <property name="testOnReturn" value="false"/>
        <property name="testWhileIdle" value="true"/>
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
        <property name="minEvictableIdleTimeMillis" value="25200000"/>
        <property name="removeAbandoned" value="true"/>
        <property name="removeAbandonedTimeout" value="1800"/>
        <property name="logAbandoned" value="true"/>
        <property name="filters" value="mergeStat"/>
    </bean>
    
    <!-- 创建数据库连接池代理,通过DataSourceProxy代理stockDataSource数据库连接池 -->
    <bean id="stockDataSourceProxy" class="io.seata.rm.datasource.DataSourceProxy">
        <constructor-arg ref="stockDataSource"/>
    </bean>

    <!-- 将数据库连接池代理stockDataSourceProxy注入到JdbcTemplate数据库操作组件中-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="stockDataSourceProxy"/>
    </bean>

    <dubbo:application name="dubbo-demo-stock-service">
        <dubbo:parameter key="qos.enable" value="false"/>
    </dubbo:application>
    <dubbo:registry address="zookeeper://localhost:2181" />
    <dubbo:protocol name="dubbo" port="20882"/>
    <dubbo:service interface="io.seata.samples.dubbo.service.StockService" ref="service" timeout="10000"/>
    
    <!-- 将JdbcTemplate数据库操作组件注入到StockServiceImpl中 -->
    <bean id="service" class="io.seata.samples.dubbo.service.impl.StockServiceImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>
    
    <!-- 全局事务注解扫描组件 -->
    <bean class="io.seata.spring.annotation.GlobalTransactionScanner">
        <constructor-arg value="dubbo-demo-stock-service"/>
        <constructor-arg value="my_test_tx_group"/>
    </bean>
</beans>

启动类:

复制代码
//The type Dubbo stock service starter.
public class DubboStockServiceStarter {
    //Stock service is ready. A seller add 100 stock to a sku: C00321
    public static void main(String[] args) {
        ClassPathXmlApplicationContext stockContext = new ClassPathXmlApplicationContext(
            new String[] {"spring/dubbo-stock-service.xml"}
        );
        stockContext.getBean("service");
        JdbcTemplate stockJdbcTemplate = (JdbcTemplate)stockContext.getBean("jdbcTemplate");
        stockJdbcTemplate.update("delete from stock_tbl where commodity_code = 'C00321'");
        stockJdbcTemplate.update("insert into stock_tbl(commodity_code, count) values ('C00321', 100)");
        new ApplicationKeeper(stockContext).keep();
    }
}

(4)seata-samples订单服务的配置和启动类

dubbo-order-service.xml配置文件:

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    
    <!-- 把jdbc.properties文件里的配置加载进来 -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations" value="classpath:jdbc.properties"/>
    </bean>
    
    <!-- 将配置文件里的值注入到库存服务的数据库连接池orderDataSource中 -->
    <bean name="orderDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.order.url}"/>
        <property name="username" value="${jdbc.order.username}"/>
        <property name="password" value="${jdbc.order.password}"/>
        <property name="driverClassName" value="${jdbc.order.driver}"/>
        <property name="initialSize" value="0"/>
        <property name="maxActive" value="180"/>
        <property name="minIdle" value="0"/>
        <property name="maxWait" value="60000"/>
        <property name="validationQuery" value="Select 'x' from DUAL"/>
        <property name="testOnBorrow" value="false"/>
        <property name="testOnReturn" value="false"/>
        <property name="testWhileIdle" value="true"/>
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
        <property name="minEvictableIdleTimeMillis" value="25200000"/>
        <property name="removeAbandoned" value="true"/>
        <property name="removeAbandonedTimeout" value="1800"/>
        <property name="logAbandoned" value="true"/>
        <property name="filters" value="mergeStat"/>
    </bean>
    
    <!-- 创建数据库连接池代理,通过DataSourceProxy代理stockDataSource数据库连接池 -->
    <bean id="orderDataSourceProxy" class="io.seata.rm.datasource.DataSourceProxy">
        <constructor-arg ref="orderDataSource"/>
    </bean>
    
    <!-- 将数据库连接池代理orderDataSourceProxy注入到JdbcTemplate数据库操作组件中-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="orderDataSourceProxy"/>
    </bean>

    <dubbo:application name="dubbo-demo-order-service">
        <dubbo:parameter key="qos.enable" value="false"/>
    </dubbo:application>
    <dubbo:registry address="zookeeper://localhost:2181" />
    <dubbo:protocol name="dubbo" port="20883"/>
    <dubbo:service interface="io.seata.samples.dubbo.service.OrderService" ref="service" timeout="10000"/>
    <dubbo:reference id="accountService" check="false" interface="io.seata.samples.dubbo.service.AccountService"/>
    
    <!-- 将JdbcTemplate数据库操作组件注入到OrderServiceImpl中 -->
    <bean id="service" class="io.seata.samples.dubbo.service.impl.OrderServiceImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
        <property name="accountService" ref="accountService"/>
    </bean>
    
    <!-- 全局事务注解扫描组件 -->
    <bean class="io.seata.spring.annotation.GlobalTransactionScanner">
        <constructor-arg value="dubbo-demo-order-service"/>
        <constructor-arg value="my_test_tx_group"/>
    </bean>
</beans>

启动类:

复制代码
//The type Dubbo order service starter.
public class DubboOrderServiceStarter {
    //The entry point of application.
    public static void main(String[] args) {
        //Order service is ready . Waiting for buyers to order
        ClassPathXmlApplicationContext orderContext = new ClassPathXmlApplicationContext(
            new String[] {"spring/dubbo-order-service.xml"}
        );
        orderContext.getBean("service");
        new ApplicationKeeper(orderContext).keep();
    }
}

(5)seata-samples业务服务的配置和启动类

dubbo-business.xml配置文件:

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <dubbo:application name="dubbo-demo-app">
        <dubbo:parameter key="qos.enable" value="false"/>
        <dubbo:parameter key="qos.accept.foreign.ip" value="false"/>
        <dubbo:parameter key="qos.port" value="33333"/>
    </dubbo:application>
    <dubbo:registry address="zookeeper://localhost:2181" />
    <dubbo:reference id="orderService" check="false" interface="io.seata.samples.dubbo.service.OrderService"/>
    <dubbo:reference id="stockService" check="false" interface="io.seata.samples.dubbo.service.StockService"/>

    <bean id="business" class="io.seata.samples.dubbo.service.impl.BusinessServiceImpl">
        <property name="orderService" ref="orderService"/>
        <property name="stockService" ref="stockService"/>
    </bean>
    
    <!-- 全局事务注解扫描组件 -->
    <bean class="io.seata.spring.annotation.GlobalTransactionScanner">
        <constructor-arg value="dubbo-demo-app"/>
        <constructor-arg value="my_test_tx_group"/>
    </bean>
</beans>

启动类:

复制代码
//The type Dubbo business tester.
public class DubboBusinessTester {
    //The entry point of application.
    public static void main(String[] args) {
        //The whole e-commerce platform is ready, The buyer(U100001) create an order on the sku(C00321) , the count is 2
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
            new String[] {"spring/dubbo-business.xml"}
        );
        //模拟调用下单接口
        final BusinessService business = (BusinessService)context.getBean("business");
        business.purchase("U100001", "C00321", 2);
    }
}

2.seata-samples业务服务启动时的核心工作

BusinessService业务服务启动时,会创建两个服务接口的动态代理。一个是OrderService订单服务接口的Dubbo动态代理,另一个是StockService库存服务接口的Dubbo动态代理。BusinessService业务服务的下单接口会添加@GlobalTransaction注解,通过@GlobalTransaction注解开启一个分布式事务,Seata的内核组件GlobalTransactionScanner就会扫描到这个注解。

复制代码
//The type Dubbo business tester.
public class DubboBusinessTester {
    //The entry point of application.
    public static void main(String[] args) {
        //The whole e-commerce platform is ready , The buyer(U100001) create an order on the sku(C00321) , the count is 2
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
            new String[] {"spring/dubbo-business.xml"}
        );
        //模拟调用下单接口
        final BusinessService business = (BusinessService)context.getBean("business");
        business.purchase("U100001", "C00321", 2);
    }
}

public class BusinessServiceImpl implements BusinessService {
    private static final Logger LOGGER = LoggerFactory.getLogger(BusinessService.class);
    private StockService stockService;
    private OrderService orderService;
    private Random random = new Random();

    @Override
    @GlobalTransactional(timeoutMills = 300000, name = "dubbo-demo-tx")//分布式事务如果5分钟还没跑完,就是超时
    public void purchase(String userId, String commodityCode, int orderCount) {
        LOGGER.info("purchase begin ... xid: " + RootContext.getXID());
        stockService.deduct(commodityCode, orderCount);
        orderService.create(userId, commodityCode, orderCount);
        if (random.nextBoolean()) {
            throw new RuntimeException("random exception mock!");
        }
    }
    
    //Sets stock service.
    public void setStockService(StockService stockService) {
        this.stockService = stockService;
    }
    
    //Sets order service.
    public void setOrderService(OrderService orderService) {
        this.orderService = orderService;
    }
}

3.seata-samples库存服务的连接池配置

首先会把jdbc.properties文件里的配置加载进来,然后将配置配置的值注入到库存服务的数据库连接池,接着通过Seata的DataSourceProxy对数据库连接池进行代理。

一.启动类

复制代码
//The type Dubbo stock service starter.
public class DubboStockServiceStarter {
    //Stock service is ready. A seller add 100 stock to a sku: C00321
    public static void main(String[] args) {
        ClassPathXmlApplicationContext stockContext = new ClassPathXmlApplicationContext(
            new String[] {"spring/dubbo-stock-service.xml"}
        );
        stockContext.getBean("service");
        JdbcTemplate stockJdbcTemplate = (JdbcTemplate)stockContext.getBean("jdbcTemplate");
        stockJdbcTemplate.update("delete from stock_tbl where commodity_code = 'C00321'");
        stockJdbcTemplate.update("insert into stock_tbl(commodity_code, count) values ('C00321', 100)");
        new ApplicationKeeper(stockContext).keep();
    }
}

二.dubbo-stock-service.xml文件

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    
    <!-- 把jdbc.properties文件里的配置加载进来 -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations" value="classpath:jdbc.properties"/>
    </bean>
    
    <!-- 将配置文件里的值注入到库存服务的数据库连接池stockDataSource中 -->
    <bean name="stockDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.stock.url}"/>
        <property name="username" value="${jdbc.stock.username}"/>
        <property name="password" value="${jdbc.stock.password}"/>
        <property name="driverClassName" value="${jdbc.stock.driver}"/>
        <property name="initialSize" value="0"/>
        <property name="maxActive" value="180"/>
        <property name="minIdle" value="0"/>
        <property name="maxWait" value="60000"/>
        <property name="validationQuery" value="Select 'x' from DUAL"/>
        <property name="testOnBorrow" value="false"/>
        <property name="testOnReturn" value="false"/>
        <property name="testWhileIdle" value="true"/>
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
        <property name="minEvictableIdleTimeMillis" value="25200000"/>
        <property name="removeAbandoned" value="true"/>
        <property name="removeAbandonedTimeout" value="1800"/>
        <property name="logAbandoned" value="true"/>
        <property name="filters" value="mergeStat"/>
    </bean>
    
    <!-- 创建数据库连接池代理,通过DataSourceProxy代理stockDataSource数据库连接池 -->
    <bean id="stockDataSourceProxy" class="io.seata.rm.datasource.DataSourceProxy">
        <constructor-arg ref="stockDataSource"/>
    </bean>
    
    <!-- 将数据库连接池代理stockDataSourceProxy注入到JdbcTemplate数据库操作组件中-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="stockDataSourceProxy"/>
    </bean>
    <dubbo:application name="dubbo-demo-stock-service">
        <dubbo:parameter key="qos.enable" value="false"/>
    </dubbo:application>
    <dubbo:registry address="zookeeper://localhost:2181" />
    <dubbo:protocol name="dubbo" port="20882"/>
    <dubbo:service interface="io.seata.samples.dubbo.service.StockService" ref="service" timeout="10000"/>
    
    <!-- 将JdbcTemplate数据库操作组件注入到StockServiceImpl中 -->
    <bean id="service" class="io.seata.samples.dubbo.service.impl.StockServiceImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>
    
    <!-- 全局事务注解扫描组件 -->
    <bean class="io.seata.spring.annotation.GlobalTransactionScanner">
        <constructor-arg value="dubbo-demo-stock-service"/>
        <constructor-arg value="my_test_tx_group"/>
    </bean>
</beans>

三.jdbc.properties文件文件

复制代码
jdbc.account.url=jdbc:mysql://localhost:3306/seata
jdbc.account.username=root
jdbc.account.password=123456
jdbc.account.driver=com.mysql.jdbc.Driver
# stock db config
jdbc.stock.url=jdbc:mysql://localhost:3306/seata
jdbc.stock.username=root
jdbc.stock.password=123456
jdbc.stock.driver=com.mysql.jdbc.Driver
# order db config
jdbc.order.url=jdbc:mysql://localhost:3306/seata
jdbc.order.username=root
jdbc.order.password=123456
jdbc.order.driver=com.mysql.jdbc.Driver

4.Seata对数据库连接池代理配置的分析

数据库连接池代理DataSourceProxy,会注入到JdbcTemplate数据库操作组件中。这样库存或者订单服务就可以通过Spring数据库操作组件JdbcTemplate,向Seata数据库连接池代理DataSourceProxy获取一个数据库连接。然后通过数据库连接,把SQL请求发送给MySQL进行处理。

5.Dubbo RPC通信过程中传递全局事务XID

BusinessService对StockService进行RPC调用时,会传递全局事务XID。StockService便可以根据RootContext.getXID()获取到全局事务XID。

复制代码
public class BusinessServiceImpl implements BusinessService {
    private static final Logger LOGGER = LoggerFactory.getLogger(BusinessService.class);
    private StockService stockService;
    private OrderService orderService;
    private Random random = new Random();

    @Override
    @GlobalTransactional(timeoutMills = 300000, name = "dubbo-demo-tx")//分布式事务如果5分钟还没跑完,就是超时
    public void purchase(String userId, String commodityCode, int orderCount) {
        LOGGER.info("purchase begin ... xid: " + RootContext.getXID());
        stockService.deduct(commodityCode, orderCount);
        orderService.create(userId, commodityCode, orderCount);
        if (random.nextBoolean()) {
            throw new RuntimeException("random exception mock!");
        }
    }
    ...
}

public class StockServiceImpl implements StockService {
    private static final Logger LOGGER = LoggerFactory.getLogger(StockService.class);
    private JdbcTemplate jdbcTemplate;

    @Override
    public void deduct(String commodityCode, int count) {
        LOGGER.info("Stock Service Begin ... xid: " + RootContext.getXID());
        LOGGER.info("Deducting inventory SQL: update stock_tbl set count = count - {} where commodity_code = {}", count, commodityCode);

        jdbcTemplate.update("update stock_tbl set count = count - ? where commodity_code = ?", new Object[] {count, commodityCode});
        LOGGER.info("Stock Service End ... ");
    }
    ...
}

6.Seata跟Dubbo整合的Filter(基于SPI机制)

Seata与Dubbo整合的Filter过滤器ApacheDubboTransactionPropagationFilter会将向SeataServer注册的全局事务xid,设置到RootContext中。

7.seata-samples的AT事务例子原理流程

8.Seata核心配置文件file.conf的内容介绍

复制代码
# Seata网络通信相关的配置
transport {
    # 网络通信的类型是TCP
    type = "TCP"
    # 网络服务端使用NIO模式
    server = "NIO"
    # 是否开启心跳
    heartbeat = true
    # 是否允许Seata的客户端批量发送请求
    enableClientBatchSendRequest = true
    # 使用Netty进行网络通信时的线程配置
    threadFactory {
        bossThreadPrefix = "NettyBoss"
        workerThreadPrefix = "NettyServerNIOWorker"
        serverExecutorThread-prefix = "NettyServerBizHandler"
        shareBossWorker = false
        clientSelectorThreadPrefix = "NettyClientSelector"
        clientSelectorThreadSize = 1
        clientWorkerThreadPrefix = "NettyClientWorkerThread"
        # 用来监听和建立网络连接的Boss线程的数量
        bossThreadSize = 1
        # 默认的Worker线程数量是8
        workerThreadSize = "default"
    }
    shutdown {
        # 销毁服务端的时候的等待时间是多少秒
        wait = 3
    }
    # 序列化类型是Seata
    serialization = "seata"
    # 是否开启压缩
    compressor = "none"
}

# Seata服务端相关的配置
service {
    # 分布式事务的分组
    vgroupMapping.my_test_tx_group = "default"
    # only support when registry.type=file, please don't set multiple addresses
    default.grouplist = "127.0.0.1:8091"
    # 是否开启降级
    enableDegrade = false
    # 是否禁用全局事务
    disableGlobalTransaction = false
}

# Seata客户端相关的配置
client {
    # 数据源管理组件的配置
    rm {
        # 异步提交缓冲区的大小
        asyncCommitBufferLimit = 10000
        # 锁相关的配置:重试间隔、重试次数、回滚冲突处理
        lock {
            retryInterval = 10
            retryTimes = 30
            retryPolicyBranchRollbackOnConflict = true
        }
        reportRetryCount = 5
        tableMetaCheckEnable = false
        reportSuccessEnable = false
    }
    # 事务管理组件的配置
    tm {
        commitRetryCount = 5
        rollbackRetryCount = 5
    }
    # 回滚日志的配置
    undo {
        dataValidation = true
        logSerialization = "jackson"
        logTable = "undo_log"
    }
    # log日志的配置
    log {
        exceptionRate = 100
    }
}
相关推荐
东阳马生架构12 天前
Seata源码—7.Seata TCC模式的事务处理一
分布式·seata·分布式事务
陆小叁13 天前
若依项目集成sentinel、seata和shardingSphere
sentinel·seata·shardingsphere
东阳马生架构14 天前
Seata源码—9.Seata XA模式的事务处理
seata
东阳马生架构15 天前
Seata源码—8.Seata Saga模式的事务处理
seata
东阳马生架构16 天前
Seata源码—7.Seata TCC模式的事务处理
seata
东阳马生架构16 天前
Seata源码—6.Seata AT模式的数据源代理
seata
东阳马生架构17 天前
Seata源码—5.全局事务的创建与返回处理二
分布式·seata·分布式事务
东阳马生架构18 天前
Seata源码—5.全局事务的创建与返回处理
seata
东阳马生架构19 天前
Seata源码—4.全局事务拦截与开启事务处理
seata
东阳马生架构20 天前
Seata源码—3.全局事务注解扫描器的初始化
seata