环境准备
2.1 下载nacos并安装启动
nacos下载:https://github.com/alibaba/nacos/releases/tag/1.2.0
这里自行安装nacos,Nacos 快速入门:Quick Start for Nacos
在浏览器打开Nacos web 控制台:http://ip:8848/nacos/index.html
2.2 下载seata server 并安装启动
2.2.1 在 Seata Release 下载最新版的 Seata Server 并解压得到如下目录:
. ├──bin ├──conf ├──file_store └──lib
2.2.2 修改 conf/registry.conf 配置,
目前seata支持如下的file、nacos 、apollo、zk、consul的注册中心和配置中心。这里我们以nacos
为例。 将 type 改为 nacos
registry { # file nacos type = "nacos" nacos { serverAddr = "192.168.10.200:8848" namespace = "" cluster = "default" } file { name = "file.conf" } } config { # file、nacos 、apollo、zk、consul type = "nacos" nacos { serverAddr = "192.168.10.200:8848" namespace = "" } file { name = "file.conf" } }
- serverAddr = "192.168.10.200:8848" :nacos 的地址
- namespace = "" :nacos的命名空间默认为``
- cluster = "default" :集群设置未默认
default
注意: seata0.9.0之后,配置如下, 其中namespace = ""
2.2.3 启动 Seata Server
使用db 模式启动
cd .. sh ./bin/seata-server.sh
3 使用seata-spring-boot-starter案例分析
seata-spring-boot-starter
是使用springboot自动装配来简化seata-all的复杂配置。1.0.0可用于替换seata-all,GlobalTransactionScanner
自动初始化(依赖SpringUtils)若其他途径实现GlobalTransactionScanner
初始化,请保证io.seata.spring.boot.autoconfigure.util.SpringUtils
先初始化; seata-spring-boot-starter
默认开启数据源自动代理,用户若再手动配置DataSourceProxy
将会导致异常。
依赖
<dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.2.0</version> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>1.2.0</version> </dependency>
参考官网中用户购买商品的业务逻辑。也可以自己创建项目,引入依赖,配置数据源,使用注解@GlobalTransactional
就可以测试,官方整个业务逻辑由4个微服务提供支持:
- 库存服务:扣除给定商品的存储数量。
- 订单服务:根据购买请求创建订单。
- 帐户服务:借记用户帐户的余额。
- 业务服务:处理业务逻辑。
请求逻辑架构
3.1 github地址
springboot-dubbo-seata:https://github.com/lidong1665/spring-cloud-learning-example/tree/master/springboot-dubbo-seata-nacos
-
samples-common :公共模块
-
samples-account :用户账号模块
-
samples-order :订单模块
-
samples-stock :库存模块
-
samples-business :业务模块
3.2 账户服务:AccountDubboService
/** * @Author: lidong * @Description 账户服务接口 * @Date Created in 2019/9/5 16:37 */ public interface AccountDubboService { /** * 从账户扣钱 */ ObjectResponse decreaseAccount(AccountDTO accountDTO); }
3.3 订单服务:OrderDubboService
/** * @Author: lidong * @Description 订单服务接口 * @Date Created in 2019/9/5 16:28 */ public interface OrderDubboService { /** * 创建订单 */ ObjectResponse<OrderDTO> createOrder(OrderDTO orderDTO); }
3.4 库存服务:StockDubboService
/** * @Author: lidong * @Description 库存服务 * @Date Created in 2019/9/5 16:22 */ public interface StockDubboService { /** * 扣减库存 */ ObjectResponse decreaseStock(CommodityDTO commodityDTO); }
3.5 业务服务:BusinessService
/** * @Author: lidong * @Description * @Date Created in 2019/9/5 17:17 */ public interface BusinessService { /** * 出处理业务服务 * @param businessDTO * @return */ ObjectResponse handleBusiness(BusinessDTO businessDTO); }
业务逻辑的具体实现主要体现在 订单服务的实现和业务服务的实现
订单服务的实现
@Service public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> implements ITOrderService { @Reference(version = "1.0.0") private AccountDubboService accountDubboService; /** * 创建订单 * @Param: OrderDTO 订单对象 * @Return: OrderDTO 订单对象 */ @Override public ObjectResponse<OrderDTO> createOrder(OrderDTO orderDTO) { ObjectResponse<OrderDTO> response = new ObjectResponse<>(); //扣减用户账户 AccountDTO accountDTO = new AccountDTO(); accountDTO.setUserId(orderDTO.getUserId()); accountDTO.setAmount(orderDTO.getOrderAmount()); ObjectResponse objectResponse = accountDubboService.decreaseAccount(accountDTO); //生成订单号 orderDTO.setOrderNo(UUID.randomUUID().toString().replace("-","")); //生成订单 TOrder tOrder = new TOrder(); BeanUtils.copyProperties(orderDTO,tOrder); tOrder.setCount(orderDTO.getOrderCount()); tOrder.setAmount(orderDTO.getOrderAmount().doubleValue()); try { baseMapper.createOrder(tOrder); } catch (Exception e) { response.setStatus(RspStatusEnum.FAIL.getCode()); response.setMessage(RspStatusEnum.FAIL.getMessage()); return response; } if (objectResponse.getStatus() != 200) { response.setStatus(RspStatusEnum.FAIL.getCode()); response.setMessage(RspStatusEnum.FAIL.getMessage()); return response; } response.setStatus(RspStatusEnum.SUCCESS.getCode()); response.setMessage(RspStatusEnum.SUCCESS.getMessage()); return response; } }
整个业务的实现逻辑
@Service @Slf4j public class BusinessServiceImpl implements BusinessService{ @Reference(version = "1.0.0") private StockDubboService stockDubboService; @Reference(version = "1.0.0") private OrderDubboService orderDubboService; private boolean flag; /** * 处理业务逻辑 * @Param: * @Return: */ @GlobalTransactional(timeoutMills = 300000, name = "dubbo-gts-seata-example") @Override public ObjectResponse handleBusiness(BusinessDTO businessDTO) { log.info("开始全局事务,XID = " + RootContext.getXID()); ObjectResponse<Object> objectResponse = new ObjectResponse<>(); //1、扣减库存 CommodityDTO commodityDTO = new CommodityDTO(); commodityDTO.setCommodityCode(businessDTO.getCommodityCode()); commodityDTO.setCount(businessDTO.getCount()); ObjectResponse stockResponse = stockDubboService.decreaseStock(commodityDTO); //2、创建订单 OrderDTO orderDTO = new OrderDTO(); orderDTO.setUserId(businessDTO.getUserId()); orderDTO.setCommodityCode(businessDTO.getCommodityCode()); orderDTO.setOrderCount(businessDTO.getCount()); orderDTO.setOrderAmount(businessDTO.getAmount()); ObjectResponse<OrderDTO> response = orderDubboService.createOrder(orderDTO); //打开注释测试事务发生异常后,全局回滚功能 // if (!flag) { // throw new RuntimeException("测试抛异常后,分布式事务回滚!"); // } if (stockResponse.getStatus() != 200 || response.getStatus() != 200) { throw new DefaultException(RspStatusEnum.FAIL); } objectResponse.setStatus(RspStatusEnum.SUCCESS.getCode()); objectResponse.setMessage(RspStatusEnum.SUCCESS.getMessage()); objectResponse.setData(response.getData()); return objectResponse; } }
3.6 使用seata的分布式事务解决方案处理dubbo的分布式事务
我们只需要在业务处理的方法handleBusiness
添加一个注解 @GlobalTransactional
@GlobalTransactional(timeoutMills = 300000, name = "dubbo-gts-seata-example") @Override public ObjectResponse handleBusiness(BusinessDTO businessDTO) { }
timeoutMills
: 超时时间name
:事务名称
3.7 准备数据库
注意: MySQL必须使用InnoDB engine
.
创建数据库 并导入数据库脚本
DROP DATABASE IF EXISTS seata; CREATE DATABASE seata; USE seata; DROP TABLE IF EXISTS `t_account`; CREATE TABLE `t_account` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` varchar(255) DEFAULT NULL, `amount` double(14,2) DEFAULT '0.00', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; -- ---------------------- -- Records of t_account -- ---------------------------- INSERT INTO `t_account` VALUES ('1', '1', '4000.00'); -- ---------------------------- -- Table structure for t_order -- ---------------------------- DROP TABLE IF EXISTS `t_order`; CREATE TABLE `t_order` ( `id` int(11) NOT NULL AUTO_INCREMENT, `order_no` varchar(255) DEFAULT NULL, `user_id` varchar(255) DEFAULT NULL, `commodity_code` varchar(255) DEFAULT NULL, `count` int(11) DEFAULT '0', `amount` double(14,2) DEFAULT '0.00', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=64 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of t_order -- ---------------------------- -- ---------------------------- -- Table structure for t_stock -- ---------------------------- DROP TABLE IF EXISTS `t_stock`; CREATE TABLE `t_stock` ( `id` int(11) NOT NULL AUTO_INCREMENT, `commodity_code` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, `count` int(11) DEFAULT '0', PRIMARY KEY (`id`), UNIQUE KEY `commodity_code` (`commodity_code`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of t_stock -- ---------------------------- INSERT INTO `t_stock` VALUES ('1', 'C201901140001', '水杯', '1000'); -- ---------------------------- -- Table structure for undo_log -- 注意此处0.3.0+ 增加唯一索引 ux_undo_log -- ---------------------------- DROP TABLE IF EXISTS `undo_log`; CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) NOT NULL, `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int(11) NOT NULL, `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, `ext` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of undo_log -- ---------------------------- SET FOREIGN_KEY_CHECKS=1;
会看到如上的4个表
+-------------------------+ | Tables_in_seata | +-------------------------+ | t_account | | t_order | | t_stock | | undo_log | +-------------------------+
这里为了简化我将这个三张表创建到一个库中,使用是三个数据源来实现。
3.8 我们以账号服务samples-account
为例 ,分析需要注意的配置项目
3.8.1 引入的依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <artifactId>springboot-dubbo-seata</artifactId> <packaging>pom</packaging> <name>springboot-dubbo-seata</name> <groupId>io.seata</groupId> <version>1.1.0</version> <description>Demo project for Spring Cloud Alibaba Dubbo</description> <modules> <module>samples-common</module> <module>samples-account</module> <module>samples-order</module> <module>samples-stock</module> <module>samples-business</module> </modules> <properties> <springboot.verison>2.2.2.RELEASE</springboot.verison> <java.version>1.8</java.version> <druid.version>1.1.10</druid.version> <mybatis.version>1.3.2</mybatis.version> <mybatis-plus.version>2.3</mybatis-plus.version> <nacos.version>0.2.3</nacos.version> <lombok.version>1.16.22</lombok.version> <dubbo.version>2.7.5</dubbo.version> <nacos-client.verison>1.1.3</nacos-client.verison> <seata.version>x.x.x</seata.version> <netty.version>4.1.32.Final</netty.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${springboot.verison}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>${springboot.verison}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>${springboot.verison}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>${mybatis-plus.version}</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.version}</version> <exclusions> <exclusion> <artifactId>spring</artifactId> <groupId>org.springframework</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>${dubbo.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo-config-spring --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-config-spring</artifactId> <version>${dubbo.version}</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-registry-nacos</artifactId> <version>${dubbo.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/io.seata/seata-all --> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>${seata.version}</version> </dependency> <dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> <version>${nacos-client.verison}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${springboot.verison}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>${netty.version}</version> </dependency> <dependency> <groupId>com.alibaba.spring</groupId> <artifactId>spring-context-support</artifactId> <version>1.0.5</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> <configuration> <skip>true</skip> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> </plugins> </build> </project>
注意:
seata-spring-boot-starter
: 这个是spring-boot seata 所需的主要依赖,1.0.0版本开始加入支持。dubbo-spring-boot-starter
: springboot dubbo的依赖
其他的就不一一介绍,其他的一目了然,就知道是干什么的。
3.8.2 application.yml配置
server: port: 8102 #====================================datasource ============================================= spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/seata?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true username: root password: 123456 application: name: dubbo-account-example #====================================Dubbo config=============================================== dubbo: application: id: dubbo-account-example name: dubbo-account-example qosEnable: false protocol: id: dubbo name: dubbo port: 20883 registry: id: dubbo-account-example-registry address: nacos://127.0.0.1:8848 config-center: address: nacos://127.0.0.1:8848 metadata-report: address: nacos://127.0.0.1:8848 #====================================mybatis-plus config=============================================== mybatis-plus: mapper-locations: classpath*:/mapper/*.xml typeAliasesPackage: io.seata.samples.integration.*.entity configuration: map-underscore-to-camel-case: true global-config: db-config: id-type: auto #====================================Seata Config=============================================== seata: enabled: true application-id: account-seata-example tx-service-group: account-service-seata-service-group # 事务群组(可以每个应用独立取名,也可以使用相同的名字) client: rm-report-success-enable: true rm-table-meta-check-enable: false # 自动刷新缓存中的表结构(默认false) rm-report-retry-count: 5 # 一阶段结果上报TC重试次数(默认5) rm-async-commit-buffer-limit: 10000 # 异步提交缓存队列长度(默认10000) rm: lock: lock-retry-internal: 10 # 校验或占用全局锁重试间隔(默认10ms) lock-retry-times: 30 # 校验或占用全局锁重试次数(默认30) lock-retry-policy-branch-rollback-on-conflict: true # 分支事务与其它全局回滚事务冲突时锁策略(优先释放本地锁让回滚成功) tm-commit-retry-count: 3 # 一阶段全局提交结果上报TC重试次数(默认1次,建议大于1) tm-rollback-retry-count: 3 # 一阶段全局回滚结果上报TC重试次数(默认1次,建议大于1) undo: undo-data-validation: true # 二阶段回滚镜像校验(默认true开启) undo-log-serialization: jackson # undo序列化方式(默认jackson) undo-log-table: undo_log # 自定义undo表名(默认undo_log) log: exceptionRate: 100 # 日志异常输出概率(默认100) support: spring: datasource-autoproxy: true service: vgroup-mapping: default # TC 集群(必须与seata-server保持一致) enable-degrade: false # 降级开关 disable-global-transaction: false # 禁用全局事务(默认false) grouplist: 127.0.0.1:8091 transport: shutdown: wait: 3 thread-factory: boss-thread-prefix: NettyBoss worker-thread-prefix: NettyServerNIOWorker server-executor-thread-prefix: NettyServerBizHandler share-boss-worker: false client-selector-thread-prefix: NettyClientSelector client-selector-thread-size: 1 client-worker-thread-prefix: NettyClientWorkerThread type: TCP server: NIO heartbeat: true serialization: seata compressor: none enable-client-batch-send-request: true # 客户端事务消息请求是否批量合并发送(默认true) registry: file: name: file.conf type: nacos nacos: server-addr: localhost:8848 namespace: cluster: default config: file: name: file.conf type: nacos nacos: namespace: server-addr: localhost:8848
3.8.3 SeataDataSourceAutoConfig 配置
package io.seata.samples.integration.account.config; import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import javax.sql.DataSource; /** * @Author: lidong * @Description seata global configuration * @Date Created in 2019/9/05 10:28 */ @Configuration public class SeataDataSourceAutoConfig { /** * autowired datasource config */ @Autowired private DataSourceProperties dataSourceProperties; /** * init durid datasource * * @Return: druidDataSource datasource instance */ @Bean @Primary public DruidDataSource druidDataSource(){ DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setUrl(dataSourceProperties.getUrl()); druidDataSource.setUsername(dataSourceProperties.getUsername()); druidDataSource.setPassword(dataSourceProperties.getPassword()); druidDataSource.setDriverClassName(dataSourceProperties.getDriverClassName()); druidDataSource.setInitialSize(0); druidDataSource.setMaxActive(180); druidDataSource.setMaxWait(60000); druidDataSource.setMinIdle(0); druidDataSource.setValidationQuery("Select 1 from DUAL"); druidDataSource.setTestOnBorrow(false); druidDataSource.setTestOnReturn(false); druidDataSource.setTestWhileIdle(true); druidDataSource.setTimeBetweenEvictionRunsMillis(60000); druidDataSource.setMinEvictableIdleTimeMillis(25200000); druidDataSource.setRemoveAbandoned(true); druidDataSource.setRemoveAbandonedTimeout(1800); druidDataSource.setLogAbandoned(true); return druidDataSource; } /** * init mybatis sqlSessionFactory * @Param: dataSourceProxy datasource proxy * @Return: DataSourceProxy datasource proxy */ @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources("classpath*:/mapper/*.xml")); return factoryBean.getObject(); } }
3.8.4 AccountExampleApplication 启动类的配置
package io.seata.samples.integration.account; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.config.ConfigFileApplicationListener; @SpringBootApplication(scanBasePackages = "io.seata.samples.integration.account") @MapperScan({"io.seata.samples.integration.account.mapper"}) @EnableDubbo(scanBasePackages = "io.seata.samples.integration.account") public class AccountExampleApplication { public static void main(String[] args) { SpringApplication.run(AccountExampleApplication.class, args); } }
-
@EnableDubbo
等同于@DubboComponentScan
和@EnableDubboConfig
组合 -
@DubboComponentScan
扫描 classpaths 下类中添加了@Service
和@Reference
将自动注入到spring beans中。 -
@EnableDubboConfig 扫描的dubbo的外部化配置。
4 启动所有的sample模块
启动 samples-account
、samples-order
、samples-stock
、samples-business
并且在nocos的控制台查看注册情况: http://192.168.10.200:8848/nacos/#/serviceManagement
我们可以看到上面的服务都已经注册成功。
5 测试
5. 1 发送一个下单请求
使用postman 发送 :http://localhost:8104/business/dubbo/buy
参数:
{ "userId":"1", "commodityCode":"C201901140001", "name":"fan", "count":50, "amount":"100" }
返回
{ "status": 200, "message": "成功", "data": null }
这时候控制台:
2020-01-08 10:49:37.693 INFO 24032 --- [nio-8104-exec-2] i.s.s.i.c.controller.BusinessController : 请求参数:BusinessDTO(userId=1, commodityCode=C201901140001, name=fan, count=50, amount=100)
2020-01-08 10:49:50.866 INFO 24032 --- [nio-8104-exec-2] i.s.common.loader.EnhancedServiceLoader : load ContextCore[null] extension by class[io.seata.core.context.ThreadLocalContextCore]
2020-01-08 10:49:53.619 INFO 24032 --- [nio-8104-exec-2] i.s.common.loader.EnhancedServiceLoader : load TransactionManager[null] extension by class[io.seata.tm.DefaultTransactionManager]
2020-01-08 10:49:53.620 INFO 24032 --- [nio-8104-exec-2] io.seata.tm.TransactionManagerHolder : TransactionManager Singleton io.seata.tm.DefaultTransactionManager@17f4ccad
2020-01-08 10:49:55.553 INFO 24032 --- [nio-8104-exec-2] i.s.common.loader.EnhancedServiceLoader : load LoadBalance[null] extension by class[io.seata.discovery.loadbalance.RandomLoadBalance]
2020-01-08 10:49:58.044 INFO 24032 --- [nio-8104-exec-2] i.seata.tm.api.DefaultGlobalTransaction : Begin new global transaction [192.168.10.103:8091:2032177946]
2020-01-08 10:50:00.807 INFO 24032 --- [nio-8104-exec-2] i.s.s.i.c.service.BusinessServiceImpl : 开始全局事务,XID = 192.168.10.103:8091:2032177946
2020-01-08 10:50:20.235 INFO 24032 --- [nio-8104-exec-2] i.seata.tm.api.DefaultGlobalTransaction : [192.168.10.103:8091:2032177946] commit status: Committed
事务提交成功,
我们来看一下数据库数据变化
t_account
t_order
t_stock
数据没有问题。
5.2 测试回滚
我们samples-business
将BusinessServiceImpl
的handleBusiness2
下面的代码去掉注释
if (!flag) {
throw new RuntimeException("测试抛异常后,分布式事务回滚!");
}
使用postman 发送 :http://localhost:8104/business/dubbo/buy2
.响应结果:
{ "timestamp": "2019-09-05T04:29:34.178+0000", "status": 500, "error": "Internal Server Error", "message": "测试抛异常后,分布式事务回滚!", "path": "/business/dubbo/buy" }
5.2.1 business控制台日志
2020-01-08 11:05:21.593 INFO 8168 --- [nio-8104-exec-4] i.s.s.i.c.controller.BusinessController : 请求参数:BusinessDTO(userId=1, commodityCode=C201901140001, name=fan, count=50, amount=100)
2020-01-08 11:05:25.443 INFO 8168 --- [nio-8104-exec-4] i.s.common.loader.EnhancedServiceLoader : load ContextCore[null] extension by class[io.seata.core.context.ThreadLocalContextCore]
2020-01-08 11:05:27.148 INFO 8168 --- [nio-8104-exec-4] i.s.common.loader.EnhancedServiceLoader : load TransactionManager[null] extension by class[io.seata.tm.DefaultTransactionManager]
2020-01-08 11:05:27.148 INFO 8168 --- [nio-8104-exec-4] io.seata.tm.TransactionManagerHolder : TransactionManager Singleton io.seata.tm.DefaultTransactionManager@64c90f86
2020-01-08 11:05:28.958 INFO 8168 --- [nio-8104-exec-4] i.s.common.loader.EnhancedServiceLoader : load LoadBalance[null] extension by class[io.seata.discovery.loadbalance.RandomLoadBalance]
2020-01-08 11:05:29.070 INFO 8168 --- [nio-8104-exec-4] i.seata.tm.api.DefaultGlobalTransaction : Begin new global transaction [192.168.10.103:8091:2032180177]
2020-01-08 11:05:30.310 INFO 8168 --- [nio-8104-exec-4] i.s.s.i.c.service.BusinessServiceImpl : 开始全局事务,XID = 192.168.10.103:8091:2032180177
2020-01-08 11:05:37.826 INFO 8168 --- [nio-8104-exec-4] i.seata.tm.api.DefaultGlobalTransaction : [192.168.10.103:8091:2032180177] rollback status: Rollbacked
2020-01-08 11:05:39.492 ERROR 8168 --- [nio-8104-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: 测试抛异常后,分布式事务回滚!] with root cause
java.lang.RuntimeException: 测试抛异常后,分布式事务回滚!
at io.seata.samples.integration.call.service.BusinessServiceImpl.handleBusiness2(BusinessServiceImpl.java:93) ~[classes/:na]
at io.seata.samples.integration.call.service.BusinessServiceImpl$$FastClassBySpringCGLIB$$2ab3d645.invoke(<generated>) ~[classes/:na]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769) ~[spring-aop-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747) ~[spring-aop-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at io.seata.spring.annotation.GlobalTransactionalInterceptor$1.execute(GlobalTransactionalInterceptor.java:109) ~[seata-all-1.0.0.jar:1.0.0]
at io.seata.tm.api.TransactionalTemplate.execute(TransactionalTemplate.java:64) ~[seata-all-1.0.0.jar:1.0.0]
at io.seata.spring.annotation.GlobalTransactionalInterceptor.handleGlobalTransaction(GlobalTransactionalInterceptor.java:106) ~[seata-all-1.0.0.jar:1.0.0]
at io.seata.spring.annotation.GlobalTransactionalInterceptor.invoke(GlobalTransactionalInterceptor.java:81) ~[seata-all-1.0.0.jar:1.0.0]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747) ~[spring-aop-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689) ~[spring-aop-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at io.seata.samples.integration.call.service.BusinessServiceImpl$$EnhancerBySpringCGLIB$$3ca9d82e.handleBusiness2(<generated>) ~[classes/:na]
at io.seata.samples.integration.call.controller.BusinessController.handleBusiness2(BusinessController.java:48) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_144]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_144]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_144]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_144]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591) [tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.29.jar:9.0.29]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_144]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_144]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.29.jar:9.0.29]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_144]
5.2.2 account服务控制台日志
2020-01-08 11:05:35.523 INFO 23416 --- [:20883-thread-8] i.s.s.i.a.dubbo.AccountDubboServiceImpl : 全局事务id :192.168.10.103:8091:2032180177 2020-01-08 11:05:35.793 INFO 23416 --- [:20883-thread-8] i.s.common.loader.EnhancedServiceLoader : load LoadBalance[null] extension by class[io.seata.discovery.loadbalance.RandomLoadBalance] 2020-01-08 11:05:35.908 WARN 23416 --- [:20883-thread-8] i.s.common.loader.EnhancedServiceLoader : load [io.seata.rm.datasource.undo.parser.ProtostuffUndoLogParser] class fail. io/protostuff/runtime/RuntimeEnv 2020-01-08 11:05:35.909 INFO 23416 --- [:20883-thread-8] i.s.common.loader.EnhancedServiceLoader : load UndoLogParser[jackson] extension by class[io.seata.rm.datasource.undo.parser.JacksonUndoLogParser] 2020-01-08 11:05:37.281 INFO 23416 --- [atch_RMROLE_1_8] i.s.core.rpc.netty.RmMessageListener : onMessage:xid=192.168.10.103:8091:2032180177,branchId=2032180189,branchType=AT,resourceId=jdbc:mysql://127.0.0.1:3306/seata,applicationData=null 2020-01-08 11:05:37.283 INFO 23416 --- [atch_RMROLE_1_8] io.seata.rm.AbstractRMHandler : Branch Rollbacking: 192.168.10.103:8091:2032180177 2032180189 jdbc:mysql://127.0.0.1:3306/seata 2020-01-08 11:05:37.477 INFO 23416 --- [atch_RMROLE_1_8] i.s.r.d.undo.AbstractUndoLogManager : xid 192.168.10.103:8091:2032180177 branch 2032180189, undo_log deleted with GlobalFinished 2020-01-08 11:05:37.478 INFO 23416 --- [atch_RMROLE_1_8] io.seata.rm.AbstractRMHandler : Branch Rollbacked result: PhaseTwo_Rollbacked
5.2.3 order服务控制台日志
2020-01-08 11:05:35.492 INFO 17296 --- [:20880-thread-2] i.s.s.i.o.dubbo.OrderDubboServiceImpl : 全局事务id :192.168.10.103:8091:2032180177 2020-01-08 11:05:36.470 INFO 17296 --- [:20880-thread-2] i.s.common.loader.EnhancedServiceLoader : load LoadBalance[null] extension by class[io.seata.discovery.loadbalance.RandomLoadBalance] 2020-01-08 11:05:36.648 WARN 17296 --- [:20880-thread-2] i.s.common.loader.EnhancedServiceLoader : load [io.seata.rm.datasource.undo.parser.ProtostuffUndoLogParser] class fail. io/protostuff/runtime/RuntimeEnv 2020-01-08 11:05:36.650 INFO 17296 --- [:20880-thread-2] i.s.common.loader.EnhancedServiceLoader : load UndoLogParser[jackson] extension by class[io.seata.rm.datasource.undo.parser.JacksonUndoLogParser] 2020-01-08 11:05:36.895 INFO 17296 --- [atch_RMROLE_1_8] i.s.core.rpc.netty.RmMessageListener : onMessage:xid=192.168.10.103:8091:2032180177,branchId=2032180192,branchType=AT,resourceId=jdbc:mysql://127.0.0.1:3306/seata,applicationData=null 2020-01-08 11:05:36.897 INFO 17296 --- [atch_RMROLE_1_8] io.seata.rm.AbstractRMHandler : Branch Rollbacking: 192.168.10.103:8091:2032180177 2032180192 jdbc:mysql://127.0.0.1:3306/seata 2020-01-08 11:05:37.152 INFO 17296 --- [atch_RMROLE_1_8] i.s.r.d.undo.AbstractUndoLogManager : xid 192.168.10.103:8091:2032180177 branch 2032180192, undo_log deleted with GlobalFinished 2020-01-08 11:05:37.153 INFO 17296 --- [atch_RMROLE_1_8] io.seata.rm.AbstractRMHandler : Branch Rollbacked result: PhaseTwo_Rollbacked
5.2.4 order服务控制台日志
2020-01-08 11:05:31.478 INFO 24100 --- [:20888-thread-2] i.s.common.loader.EnhancedServiceLoader : load ContextCore[null] extension by class[io.seata.core.context.ThreadLocalContextCore] 2020-01-08 11:05:31.478 INFO 24100 --- [:20888-thread-2] i.s.s.i.s.dubbo.StockDubboServiceImpl : 全局事务id :192.168.10.103:8091:2032180177 2020-01-08 11:05:32.097 INFO 24100 --- [:20888-thread-2] i.s.common.loader.EnhancedServiceLoader : load LoadBalance[null] extension by class[io.seata.discovery.loadbalance.RandomLoadBalance] 2020-01-08 11:05:33.130 WARN 24100 --- [:20888-thread-2] i.s.common.loader.EnhancedServiceLoader : load [io.seata.rm.datasource.undo.parser.ProtostuffUndoLogParser] class fail. io/protostuff/runtime/RuntimeEnv 2020-01-08 11:05:33.131 INFO 24100 --- [:20888-thread-2] i.s.common.loader.EnhancedServiceLoader : load UndoLogParser[jackson] extension by class[io.seata.rm.datasource.undo.parser.JacksonUndoLogParser] 2020-01-08 11:05:37.549 INFO 24100 --- [atch_RMROLE_1_8] i.s.core.rpc.netty.RmMessageListener : onMessage:xid=192.168.10.103:8091:2032180177,branchId=2032180182,branchType=AT,resourceId=jdbc:mysql://127.0.0.1:3306/seata,applicationData=null 2020-01-08 11:05:37.551 INFO 24100 --- [atch_RMROLE_1_8] io.seata.rm.AbstractRMHandler : Branch Rollbacking: 192.168.10.103:8091:2032180177 2032180182 jdbc:mysql://127.0.0.1:3306/seata 2020-01-08 11:05:37.692 INFO 24100 --- [atch_RMROLE_1_8] i.s.r.d.undo.AbstractUndoLogManager : xid 192.168.10.103:8091:2032180177 branch 2032180182, undo_log deleted with GlobalFinished 2020-01-08 11:05:37.693 INFO 24100 --- [atch_RMROLE_1_8] io.seata.rm.AbstractRMHandler : Branch Rollbacked result: PhaseTwo_Rollbac
1. 添加依赖
添加Spring Cloud Alibaba 依赖管理工具和 Seata 依赖
Gradle
Maven
需要注意的是Spring Cloud Alibaba 的毕业版本的 GroupId 是 com.alibaba.cloud
spring-cloud-starter-alibaba-seata
这个依赖中只依赖了spring-cloud-alibaba-seata
,所以在项目中添加spring-cloud-starter-alibaba-seata
和spring-cloud-alibaba-seata
是一样的
2. 添加Seata 配置文件
registry.conf
该配置用于指定 TC 的注册中心和配置文件,默认都是 file; 如果使用其他的注册中心,要求 Seata-Server 也注册到该配置中心上
registry.conf
file.conf
该配置用于指定TC的相关属性;如果使用注册中心也可以将配置添加到配置中心
file.conf
需要注意的是 service.vgroup_mapping
这个配置,在 Spring Cloud 中默认是${spring.application.name}-fescar-service-group
,可以通过指定application.properties
的 spring.cloud.alibaba.seata.tx-service-group
这个属性覆盖,但是必须要和 file.conf
中的一致,否则会提示 no available server to connect
3. 注入数据源
Seata 通过代理数据源的方式实现分支事务;MyBatis 和 JPA 都需要注入 io.seata.rm.datasource.DataSourceProxy
, 不同的是,MyBatis 还需要额外注入 org.apache.ibatis.session.SqlSessionFactory
MyBatis
JPA
如果使用的是 Hikari 数据源,需要修改数据源的配置,以及注入的 Bean 的配置前缀
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.jdbc-url=jdbc:mysql://localhost:3306/seata?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.hikari.username=root
spring.datasource.hikari.password=123456
@Bean @ConfigurationProperties(prefix = "spring.datasource.hikari") public DataSource dataSource() { return new HikariDataSource(); }
4. 添加 undo_log 表
在业务相关的数据库中添加 undo_log 表,用于保存需要回滚的数据
CREATE TABLE `undo_log` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `branch_id` BIGINT(20) NOT NULL, `xid` VARCHAR(100) NOT NULL, `context` VARCHAR(128) NOT NULL, `rollback_info` LONGBLOB NOT NULL, `log_status` INT(11) NOT NULL, `log_created` DATETIME NOT NULL, `log_modified` DATETIME NOT NULL, `ext` VARCHAR(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8
application.yml
## seata配置
seata:
enabled: true
application-id: seata-server
# 下面这个需要跟刚刚config.txt上传到nacos配置中心的相同
# config.txt中:service.vgroupmapping.default_tx_group=default(自己看,自己理解)
tx-service-group: my_test_tx_group
# 这里开启自动的数据源配置,否则后秒要自己手动配置
enable-auto-data-source-proxy: true
data-source-proxy-mode: at
use-jdk-proxy: false
service:
vgroup-mapping.my_test_tx_group: default
grouplist.default: localhost:8091
脚本:
CREATE TABLE IF NOT EXISTS `undo_log`
(
`branch_id` BIGINT(20) NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(100) NOT NULL COMMENT 'global transaction id',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
CREATE TABLE IF NOT EXISTS `global_table`
(
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`status` TINYINT NOT NULL,
`application_id` VARCHAR(32),
`transaction_service_group` VARCHAR(32),
`transaction_name` VARCHAR(128),
`timeout` INT,
`begin_time` BIGINT,
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`xid`),
KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`resource_group_id` VARCHAR(32),
`resource_id` VARCHAR(256),
`branch_type` VARCHAR(8),
`status` TINYINT,
`client_id` VARCHAR(64),
`application_data` VARCHAR(2000),
`gmt_create` DATETIME(6),
`gmt_modified` DATETIME(6),
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(96),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;