- 一、seata介绍
- 1.seata安装
- 2.seata简介
- 3. 分布式架构
- 4.seata的过程ID和三组件模型
- 5. seata处理事务过程
- 二、seata集成示例
- 1.案例分析
- 2.seata管理分布式事务
- 1.引依赖
- 2.改配置文件
- 3.开启注解
- 4.使用注解
- 5.测试结果
seata
安装的安装,可以参考另外一篇博客
https://blog.csdn.net/zxd1435513775/article/details/121871487
2.seata简介seata
是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
seata
官方网址:https://seata.io/zh-cn/
在分布式之前,单机单库或者,多个微服务连接同一个数据库,事务的一致性可以得到解决,使用@Transactional
注解就好,此注解的事务是面对的同一个数据源。
但在分布式之后,单体应用被拆分成多个微服务应用,每个微服务应用都连接各自独立的数据库,分别使用自己独立的数据源,此时每个微服务内部的数据一致性由本地事务来保证,但是全局的数据一致性问题没法保证。
一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题
4.seata的过程ID和三组件模型Transaction ID XID
:全局唯一的事务ID
三组件模型
Transaction Coordinator(TC)
: 事务协调者,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚Transaction Manager(TM)
: 事务管理器,控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议Resource Manager(RM)
:资源管理器,控制分支事务,负责分支注册,状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚
-
TM
向TC
申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID
-
XID
在微服务调用链路的上下文中传播
-
RM
向TC
注册分支事务,将其纳入XID
对应全局事务的管辖
-
TM
向TC
发起针对XID
的全局提交或回滚决议
-
TC
调度XID
下管辖的全部分支事务完成提交或回滚请求
模拟场景,创建订单、修改库存数量、扣减用户余额,分别对应三个微服务:
seata-order-service
订单服务seata-storage-service
库存服务seata-account-service
账户服务
三个服务的调用顺序如下:
订单服务创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->订单服务修改订单状态
文章末尾提供示例源码,包括mysql初始化脚本,但数据库得自己创建
分别创建三个数据库,与三个服务对应:seata-order、seata-storage、seata-account
创建完三个数据库后,要执行seata
的脚本:
github地址:https://github.com/seata/seata/blob/develop/script/client/at/db/mysql.sql
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 = utf8 COMMENT ='AT transaction mode undo table';
另外要把另外三个库的初始化脚本也执行完,最后数据库结构如下:
每个服务的数据库都要创建一个 undo_log 回滚日志表
三个数据库数据的初始状态如下:
订单库没有订单
库存库有100件库存
账户库余额有1000
如果三个服务之间,没有发生异常,程序也没有集成seata
,程序正常调用,在浏览器中输入如下地址,用get
方式容易测试。正常情况下,没有问题。
http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100
订单创建成功,支付状态修改成功
库存扣减成功
账户余额扣减成功
现在,要模拟程序发送网络异常或者程序异常的情况,在seata-account-service
中,让线程阻塞,同时设置openFeign
调用超时时间为5秒
# 设置openFeign超时时间
feign.client.config.default.connect-timeout=5000
feign.client.config.default.read-timeout=5000
public void decrease(Long userId, BigDecimal money) {
log.info("------->account-service中扣减账户余额开始");
// 模拟超时
try {
TimeUnit.SECONDS.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
accountDao.decrease(userId,money);
log.info("------->account-service中扣减账户余额结束");
}
然后,再次访问刚才的地址:
http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100
会发现,seata-order-service
服务报了超时异常,但订单已经创建成功,并且库存量和账户余额都被扣减,但订单的状态仍然是未支付状态,这样是不对的!!!!
订单创建成功,但支付状态为:未支付。0表示未支付,1表示已支付。
库存扣减成功
账户余额扣减成功,但订单的状态仍为:未支付!!!
下面就需要用seata
来管理分布式事务。在做下面事情之前,要启动Nacos
和Seata
服务!!!
com.alibaba.cloud
spring-cloud-starter-alibaba-seata
2021.1
io.seata
seata-spring-boot-starter
io.seata
seata-spring-boot-starter
1.4.2
2.改配置文件
每个微服务的配置文件都要加seata
配置!!!
# seata配置
seata.enabled=true
seata.enable-auto-data-source-proxy=true
# config.txt文件中修改的vgroupMapping值
seata.tx-service-group=scorpios_tx_group
seata.registry.type=nacos
# registry.conf配置文件中 nacos的application名称
seata.registry.nacos.application=seata-server
seata.registry.nacos.server-addr=localhost:8848
seata.registry.nacos.username=nacos
seata.registry.nacos.password=nacos
seata.registry.nacos.namespace=88b8f583-43f9-4272-bd46-78a9f89c56e8
seata.registry.nacos.cluster=default
seata.registry.nacos.group=SEATA_GROUP
seata.config.type=nacos
seata.config.nacos.server-addr=localhost:8848
seata.config.nacos.namespace=88b8f583-43f9-4272-bd46-78a9f89c56e8
seata.config.nacos.group=SEATA_GROUP
seata.config.nacos.username=nacos
seata.config.nacos.password=nacos
3.开启注解
每个微服务都要开启seata
!!!
@SpringBootApplication
@MapperScan({"com.scorpios.order.dao"})
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.scorpios")
@EnableAutoDataSourceProxy // 开启seata
public class SeataOrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SeataOrderServiceApplication.class, args);
}
}
4.使用注解
在seata-order-service
的业务方法上,添加seata
分布式事务注解:@GlobalTransactional
// 使用seata的注解,支持定制化,只对哪些异常进行处理
@GlobalTransactional(name = "scorpios-create-order",rollbackFor = Exception.class)
@Override
public void create(Order order){
log.info("----->开始新建订单");
//新建订单
orderDao.create(order);
//扣减库存
log.info("----->订单微服务开始调用库存,做扣减Count");
storageService.decrease(order.getProductId(),order.getCount());
log.info("----->订单微服务开始调用库存,做扣减end");
//扣减账户
log.info("----->订单微服务开始调用账户,做扣减Money");
accountService.decrease(order.getUserId(),order.getMoney());
log.info("----->订单微服务开始调用账户,做扣减end");
//修改订单状态,从零到1代表已经完成
log.info("----->修改订单状态开始");
orderDao.update(order.getUserId(),0);
log.info("----->修改订单状态结束");
log.info("----->下订单结束了");
}
5.测试结果
再次输入如下地址:
http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100
会发现seata-order-service
会报超时异常,同时seata
已经完成数据回滚
再去观察三个数据库,发现数据并没有任何变化,此处就不贴图了,seata
集成完毕。
一句话总结,就是一个注解@GlobalTransactional
的使用,哈哈哈~
项目源码地址:https://github.com/Hofanking/springboot-seata-example