一 seata案例
1.1 背景说明
本案例使用seata的at模式,模拟分布式事务场景:【下订单,减库存,扣余额,改状态】
AT模式原理:是2pc方案的演变,
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
二阶段: 提交异步化,非常迅速地完成。如果失败进行回滚通过一阶段的回滚日志进行反向补偿。
代码地址:https://gitee.com/jurf-liu/mscloud-demo-2024.git
1.2 初始化脚本
1.2.1 新建seata_order_024 库
1.新建undol_log表
-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',`xid` VARCHAR(128) 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 = utf8mb4 COMMENT ='AT transaction mode undo table';
2.新建t_order表
CREATE TABLE t_order(`id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,`user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',`product_id` BIGINT(11)DEFAULT NULL COMMENT '产品id',`count` INT(11) DEFAULT NULL COMMENT '数量',`money` DECIMAL(11,0) DEFAULT NULL COMMENT '金额',`status` INT(1) DEFAULT NULL COMMENT '订单状态: 0:创建中; 1:已完结')ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
1.2.2 创建seata_storage_024库
1.undo.log表
-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',`xid` VARCHAR(128) 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 = utf8mb4 COMMENT ='AT transaction mode undo table';
2.新建业务表 t_storage
CREATE TABLE t_storage(`id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,`product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id',`total` INT(11) DEFAULT NULL COMMENT '总库存',`used` INT(11) DEFAULT NULL COMMENT '已用库存',`residue` INT(11) DEFAULT NULL COMMENT '剩余库存')ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;INSERT INTO t_storage(`id`,`product_id`,`total`,`used`,`residue`)VALUES('1','1','100','0','100');SELECT * FROM t_storage;
1.2.3 创建seata_account_024库
1.undo.log表
-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',`xid` VARCHAR(128) 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 = utf8mb4 COMMENT ='AT transaction mode undo table';
2.创建表 t_account
CREATE TABLE t_account(`id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'id',`user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',`total` DECIMAL(10,0) DEFAULT NULL COMMENT '总额度',`used` DECIMAL(10,0) DEFAULT NULL COMMENT '已用账户余额',`residue` DECIMAL(10,0) DEFAULT '0' COMMENT '剩余可用额度')ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;INSERT INTO t_account(`id`,`user_id`,`total`,`used`,`residue`)VALUES('1','1','1000','0','1000');SELECT * FROM t_account;
1.2.4 创建表完成后
1.3 openfegin 工具服务接口
1.AccountFeginApi
2.storageFeginApi
二 seata分布式工程搭建
2.1 工程seata-order
2.1.1.工程结构
2.1.2.pom的添加
<!-- nacos --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--alibaba-seata--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency><!--openfeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--loadbalancer--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><!--cloud-api-commons--><dependency><groupId>com.jurf.ms.api</groupId><artifactId>ms-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency><!--web + actuator--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><!--SpringBoot集成druid连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId></dependency><!-- Swagger3 调用方式 http://你的主机IP地址:5555/swagger-ui/index.html --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId></dependency><!--mybatis和springboot整合--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><!--Mysql数据库驱动8 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--persistence--><dependency><groupId>javax.persistence</groupId><artifactId>persistence-api</artifactId></dependency><!--通用Mapper4--><dependency><groupId>tk.mybatis</groupId><artifactId>mapper</artifactId></dependency><!--hutool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId></dependency><!-- fastjson2 --><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version><scope>provided</scope></dependency><!--test--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
2.1.3.核心业务的编写
2.1.4.配置文件
1.nacos配置信息
2.seata配置信息:这里配置seata服务端配置的信息,配置注册到nacos信息,见seata服务端
application配置的信息。config/application.yml中配置的注册到nacos信息。
2.1.5.启动类
这里最开始无法加载写在工具服务中,openfegin-api中的类,需要添加@componentscan进行扫描
具体原因解决办法参考:springboot注解@ComponentScan注解作用-CSDN博客
2.2 工程seata-storage
2.2.1.工程结构
2.2.2.pom的添加
<!-- nacos --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--alibaba-seata--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency><!--openfeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--loadbalancer--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><!--cloud-api-commons--><dependency><groupId>com.jurf.ms.api</groupId><artifactId>ms-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency><!--web + actuator--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><!--SpringBoot集成druid连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId></dependency><!-- Swagger3 调用方式 http://你的主机IP地址:5555/swagger-ui/index.html --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId></dependency><!--mybatis和springboot整合--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><!--Mysql数据库驱动8 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--persistence--><dependency><groupId>javax.persistence</groupId><artifactId>persistence-api</artifactId></dependency><!--通用Mapper4--><dependency><groupId>tk.mybatis</groupId><artifactId>mapper</artifactId></dependency><!--hutool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId></dependency><!-- fastjson2 --><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version><scope>provided</scope></dependency><!--test--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>tk.mybatis</groupId><artifactId>mapper-spring</artifactId><version>4.2.3</version></dependency><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-commons</artifactId><version>3.1.4</version></dependency>
2.2.3.核心业务的编写
2.2.4.配置文件
1.nacos配置信息和seata的配置信息
2.2.5.启动类
2.3 工程seata-account
2.3.1.工程结构
2.3.2.pom的添加
<!-- nacos --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--alibaba-seata--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency><!--openfeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--loadbalancer--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><!--cloud_commons_utils--><dependency><groupId>com.jurf.ms.api</groupId><artifactId>ms-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency><!--web + actuator--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><!--SpringBoot集成druid连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId></dependency><!-- Swagger3 调用方式 http://你的主机IP地址:5555/swagger-ui/index.html --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId></dependency><!--mybatis和springboot整合--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><!--Mysql数据库驱动8 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--persistence--><dependency><groupId>javax.persistence</groupId><artifactId>persistence-api</artifactId></dependency><!--通用Mapper4--><dependency><groupId>tk.mybatis</groupId><artifactId>mapper</artifactId></dependency><!--hutool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId></dependency><!-- fastjson2 --><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version><scope>provided</scope></dependency><!--test--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
2.3.3.核心业务的编写
2.3.4.配置文件
2.3.5.启动类
三 seata不同场景验证
3.1 正常访问
1.查看t_order 表 , t_storage表,t_account表中初始化数据
2.进行访问:http://localhost:222/order/create?userId=1&productId=1&count=10&money=100
3.console控制台
4.数据库表:t_order表新增一条订单记录;t_storage 表 进行了减库存;t_account表进行了账户余额修改。一切都是那么和谐
5.undolog表中业务数据
3.2 模拟异常
1.在微服务account服务,修改账户余额逻辑上,加上超时,让其报异常,如图:
2.服务启动,再次访问:
3.console报异常:
4.数据表信息: 出现不一致问题,描述如下:
a): 订单表产生数据,status=0 一直处于支付中.... ,产生问题,报异常没有回顾
b): 账户表,库存表均存在问题:订单服务报超时异常,数据表中数据没有进行回滚。
c) undo.log表,没有数据
3.3 模拟异常,@GlobalTransaction分布式事务回滚
3.3.1.在TM端添加注解:
@GlobalTransactional(name = "zzyy-create-order",rollbackFor = Exception.class) //AT
3.3.2.启动服务
1.先启动nacos
2.再启动seata
3.启动业务应用
3.3.3.验证
访问地址: http://localhost:222/order/create?userId=1&productId=1&count=10&money=100
查看console日志:index 0 out of bounds for length 0,解决办法见:springcloud第4季 seata报could not find any implementation for class-CSDN博客
查看seata服务端log:经过一番努力查看到,是由于seata2.0+alibaba.springcloud 版本不兼容问题导致,这里进行降级版本。
3.3.4 调整cloud和seata版本的兼容性
1.cloud的版本:
2.seata服务端2.0 降级到1.7。(按照2.0版本的配置,在1.7版本上进行配置一遍)
3.启动命令
E:\seata-1.7.0\bin>seata-server.bat
3.3.5.重启各个服务进行验证
3.3.5.1.访问前,数据表数据状态
seata服务端几个表数据:
a)
b)distributed_lock表
c)global_table
d)lock_table
e)tb_order库中:tb_order表中,只有3条订单记录,undolog表无数据
f)tb_storage库中:tb_storage表中,use=30;residue=70,undolog表无数据
h)tb_account库中:tb_account表中,used=300;residue=700,undolog表无数据
3.3.5.2.访问前后
http://localhost:222/order/create?userId=1&productId=1&count=10&money=100
a)查看seata服务端页面: 可以看到注册的分支事务信息
b)查看console控制台
order服务:提示二阶段回滚信息
storage服务:提示二阶段回滚信息
account服务:提示二阶段回滚信息
c) seata服务端表中数据:lock表对应有数据,全局表,分支表均有数据
d) seata客户端业务表中数据变化
tb_order表中: 没有数据变化,进行回滚没有变化
tb_storage表: 金额没有发生变化,used=30 ,residue=70,进行回滚,数据没有变化
tb_account表,used=300,residue=700,进行回滚,数据没有变化
3.3.5.3.结论
通过seata的AT模式实现了分布式事务在【下订单,减库存,扣余额,该状态】的场景成功得以实现。