Seata事务

Seata事务
虹色轨迹🌠事务
ACID特性
原子性、一致性、隔离性、永久性
单体架构中,学习过事务,但是只能保证一个项目的数据一致性,在分布式中如何保证?需要用到一个工具:seata
Seata
什么是Seata?
Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务,也是Spring Cloud Alibaba
提供的组件
Seata保证微服务远程调用业务的原子性
Seata将为用户提供了AT
、TCC
、SAGA
、XA
事务模式,为用户打造一站式的分布式解决方案
Seata的运行原理(AT模式)
Seata构成部分包含:
TC:事务协调器
TM:事务管理器(事务的起点)
RM:资源管理器
我们项目使用的AT模式(默认)来完成分布式事务的解决方案
AT模式的运行过程
- 事务的发起方
(TM)
会事务协调器(TC)
申请一个全局事务id,并保存 - Seata会管理事务中所有相关的参与方的数据源,将数据操作之前和之后的镜像都保存在
undo_log
表中,这个表是seata组件规定的表,没有它就不能实现效果,依靠它来实现提交或回滚的操作 - 事务的发起方会联通全局id一起通过远程调用运行资源管理器
(RM)
中的方法 RM
接收到全局id,去运行指定方法,并将运行结果的状态发送给TC
- 如果所有分支运行都正常,事务管理器会通过事务协调器通知所有模块执行数据库操作,真正影响数据库内容;如果有任何一个分支模块运行异常,都会通知
TC
,再由TC
通知所有分支将数据库回滚,恢复成运行之前的样子
AT模式运行有一个非常明显的前提条件,这个条件不满足就无法使用AT模式
这个条件就是事务所有分支必须是操作关系型数据库
但是如果我们在业务过程中有一个节点操作的是redis
或者其他非关系型数据库
,就无法使用AT
模式
TCC模式
简单来说,TCC
模式 就是自己编写代码来完成事务的提交或回滚
TCC
模式要求我们再每个参与方事务的业务中编写一组方法:prepare
、commit
、rollback
prepare
:无论事务成功还是失败都会运行的代码
commit
:当整体事务运行成功时运行的方法
rollback
:当整体事务失败时运行的方法
优点:虽然代码是自己编写的,但是事务整体提交或回滚的机制仍然可用(还是由TC来调度)
缺点:每个业务都要编写3个方法来对应,代码冗余,业务入侵量大
SAGA模式
SAGA
模式的思想是对应每个业务逻辑层编写一个新的类,可以设置指定的业务逻辑层方法发生异常时,运行当前编写的新类中的代码
这样编写不影响已经编写好的代码
一般用于修改已经完成的老代码
缺点是每个事务分支都要编写一个类来回滚业务,会造成类的数量较多,开发量比较大
XA模式
支持XA协议的数据库分布式事务,使用比较少
使用Seata
从百度网盘中下载Seata压缩包
解压到没有中文或空格的路径中即可,进入bin目录中有两个文件:seata-server.bat
、seata-server.sh
打开dos窗口,输入以下命令
D:\software\seata\seata\seata-server-1.4.2\bin>seata-server.bat -h 127.0.0.1 -m file
在windows系统中运行seata可能出现不稳定的情况,重启seata即可解决
配置Seata
cart/stock/order 都是具备数据操作功能的模块,都需要添加seata依赖<!--添加seata依赖-->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</dependency>
<!--Seata完成分布式事务需要的两个相关依赖(Seata需要下面两个依赖中的资源)-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
cart/stock/order 模块的application-dev.yml文件配置seata:
tx-service-group: csmall_group #定义分组名称,一般是为了区分项目
service:
vgroup-mapping:
csmall_group: default #csmall_group组使用默认的Seata配置完成事务
grouplist:
default: localhost:8091 #设置Seata所在的地址,默认端口是8091
business模块的配置更加简单,因为不需要操作数据库,所以只需要Seata的依赖<!--添加seata依赖-->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</dependency>
application-dev.ymlseata:
tx-service-group: csmall_group #定义分组名称,一般是为了区分项目
service:
vgroup-mapping:
csmall_group: default #csmall_group组使用默认的Seata配置完成事务
grouplist:
default: localhost:8091 #设置Seata所在的地址,默认端口是8091s
添加完必要配置之后,如果要启动Seata非常简单,只要在TM的业务逻辑层方法上添加专用的注解即可
//提供log对象,可以用于打印日志
public class BusinessServiceImpl implements IBusinessService {
//需要利用dubbo调用order模块中的新增订单方法
private IOrderService dubboOrderService;
//一旦编写@GlobalTransactional,标记这个方法就是seata分布式事务的起点了,也就是TM
//最终的效果就是由当前方法引发的所有远程调用对数据的操作,要么都成功,要么都失败
public void buy() {
//模拟购买业务
//创建OrderAddDTO类,并赋值
OrderAddDTO orderAddDTO = new OrderAddDTO();
orderAddDTO.setUserId("UU100");
orderAddDTO.setCommodityCode("PC100");
orderAddDTO.setCount(5);
orderAddDTO.setMoney(100);
//调用order模块的方法
dubboOrderService.orderAdd(orderAddDTO);
//目前现在控制台输出
log.info("新增订单的信息为:{}",orderAddDTO);
}
}
启动nacos和seata以及cart、stock、order、business模块进行测试,为了观察效果,添加异常代码
public void buy() {
//模拟购买业务
//创建OrderAddDTO类,并赋值
OrderAddDTO orderAddDTO = new OrderAddDTO();
orderAddDTO.setUserId("UU100");
orderAddDTO.setCommodityCode("PC100");
orderAddDTO.setCount(5);
orderAddDTO.setMoney(100);
//调用order模块的方法
dubboOrderService.orderAdd(orderAddDTO);
//目前现在控制台输出
log.info("新增订单的信息为:{}",orderAddDTO);
//为了验证seata实现的分布式事务效果
//我们在所有代码运行完毕之后,随机抛出异常,观察是否能回滚
if (Math.random()<0.5){
throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR,
"发生随机异常,本次操作回滚");
}
}