基于seata实现分布式事务demo
概述
详细
1.需求(要做什么)
模仿一个购物流程. 利用seata的分布式事务实现 订单服务, 账户服务, 库存服务的数据一致性. 订单服务创建订单同时: 调用账户服务扣钱, 调用库存服务扣减库存.
2.理论概述
Seata框架是一个业务层的XA(两阶段提交)解决方案。
下面是一个分布式事务在Seata中的执行流程:
TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。
XID 在微服务调用链路的上下文中传播。
RM 向 TC 注册分支事务,接着执行这个分支事务并提交(重点:RM在第一阶段就已经执行了本地事务的提交/回滚),最后将执行结果汇报给TC。
TM 根据 TC 中所有的分支事务的执行情况,发起全局提交或回滚决议。
TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。
第一阶段:
Seata的每一个RM维护了一张UNDO_LOG表, 其中保存了每一次本地事务的回滚数据, 所以能够在第一阶段直接提交事务。
第二阶段:
二阶段的回滚并不依赖于本地数据库事务的回滚,而是RM直接读取这张UNDO_LOG表,并将数据更新为UNDO_LOG中存储的历史数据。
3. 代码分析
1. 引入pom
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-seata</artifactId> <version>2.2.0.RELEASE</version> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>1.3.0</version> </dependency>
2. 配置seata服务
seata: application-id: ${spring.application.name} #需要和nacos中配置保持一致 tx-service-group: kevin_tx_group config: type: nacos nacos: #需要和server在同一个注册中心下 serverAddr: localhost:8848 namespace: seata-server-namespace #需要server端(registry和config)、nacos配置client端(registry和config)保持一致 group: SEATA_GROUP registry: type: nacos nacos: #需要和seata-server端保持一致,即server在nacos中的名称,默认为seata-server application: seata-server serverAddr: localhost:8848 namespace: seata-server-namespace #需要server端(registry和config)、nacos配置client端(registry和config)保持一致 group: SEATA_GROUP
3. @GlobalTransactional 全局事务注解
@Service public class OrderService { @Autowired RestTemplate restTemplate; @Autowired JdbcTemplate jdbcTemplate; @GlobalTransactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class) public void save() { String id = System.currentTimeMillis()+""; String order_no = System.currentTimeMillis()+""; Integer money = 10; Integer num = 3; jdbcTemplate.update(" INSERT INTO t_order(id, order_no, num, money) VALUES ( ?, ?, ?, ?) ", id, order_no, num, money); restTemplate.getForObject("http://localhost:8020/charge?money="+money, String.class); restTemplate.getForObject("http://localhost:8030/deduct?num="+num, String.class); } }
4.项目文件结构截图
5.安装部署
5.1 创建mysql数据库
分别创建数据 seata-order, seata-storage, seata-account.
分别执行各自脚本: 脚本在 ./seata-demo-parent/sql 目录下.
5.2 启动seata服务
在 ./seata-demo-parent/doc/ 目录下, 解压seata-1.3.0-nacos.zip,
进入seata-1.3.0-nacos/bin目录, 双击 seata-server.bat
前提 需要保证nacos启动, 且seata配置已经导入到nacos .
5.3 导入maven项目, 并启动
idea导入maven项目, 待maven依赖jar包下载完毕后, 分别启动
OrderApplication, AccountApplication, StorageApplication
6.演示效果
当库存不足, 订单生成失败, 扣款失败