文章目录
- Spring Cloud Alibaba Seata 搭建以及分布式事务的验证
- 1.seata 简介
- 2. seata的三大角色
- 3. Seata的流程
- 4. Seata AT模式
- 5. Seata搭建
- 找到模板案例,照着抄
- 6. Seata Client快速开始
- 6.1 声明式事务实现(@GlobalTransactional)
- 6.2 添加依赖
- 6.3 各微服务对应配置undo_log表
- 6.4 测试
- 6.6 只需要一个公共注解代替原始注解
Spring Cloud Alibaba Seata 搭建以及分布式事务的验证
1.seata 简介
- Seata(Simple Extensible Autonomous Transaction Architecture)是一种开源的分布式事务解决方案,旨在解决微服务架构下的分布式事务问题。它提供了一套完整的分布式事务管理功能,包括全局事务管理、本地事务协调和分布式事务恢复等。
- 在传统的单体应用中,使用关系型数据库的事务机制可以确保数据的一致性和隔离性。然而,在微服务架构中,每个微服务都有自己独立的数据库,无法直接使用传统事务进行跨服务的一致性控制。这就导致了分布式事务的挑战。
- Seata 提供了分布式事务的解决方案,通过引入一个事务协调器(Transaction Coordinator)来协调多个参与者(Participants)之间的本地事务。Seata 支持两阶段提交(Two-Phase Commit)和补偿事务(Compensating Transaction)两种模式。
- 在 Seata 的架构中,事务发起者(Transaction Coordinator)负责全局事务的创建、提交和回滚,并协调各个参与者的事务操作。参与者(Participants)执行具体的本地事务操作,并与事务协调器进行通信。事务管理器(Transaction Manager)负责协调全局和本地事务的提交和回滚,以及事务的状态管理。
- Seata 提供了与各种主流数据库和框架的集成支持,例如 MySQL、Oracle、Spring Boot 等,使得在现有的微服务架构中引入分布式事务变得更加方便。
2. seata的三大角色
- 事务协调者(Transaction Coordinator,TC)- 维护全局和分支事务的状态,驱动全局事务提交或回滚,是整个分布式事务的核心。
- 事务管理器(Transaction Manager,TM)- 定义全局事务的范围,开始全局事务、提交或回滚全局事务。
- 资源管理器(Resource Manager,RM)- 管理分支事务处理的资源,与 TC 交流以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
- 其中,TC 是单独部署的 Server 服务端,TM 和 RM 则是嵌入到应用中的 Client 客户端。由于 TC 是独立的服务端,它与 TM 和 RM 进行通信的接口被称为 RPC(Remote Procedure Call,远程过程调用)接口。具体来讲,TM 和 RM 向 TC 发送关于事务的请求,TC 则根据请求的类型进行相应的响应和操作,从而实现分布式事务的一致性和可靠性。
3. Seata的流程
- TM发起一个全局事务请求,请求TC开启一个全局事务。TC为该全局事务生成一个全局唯一的事务ID(XID)gloab_table就是存储的全局事务信息。
- RM将本地事务注册为该全局事务的分支事务,并通过XID进行关联。RM会与TC进行通信,报告分支事务的状态,分支事务存储在brance_table。
- TM根据业务逻辑执行相关操作,操作涉及的资源由RM管理。
- 当TM完成所有操作后,请求TC提交或回滚该全局事务。TC收到请求后,驱动所有参与者(RM)执行相应的提交或回滚操作。每个参与者根据TC的指令将其本地事务提交或回滚
4. Seata AT模式
-
第一阶段
业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。核心在于对业务sql进行解析,转换成undolog.
AT模式官方网址 -
第二阶段
1.分布式事务操作成功,则TC通知RM异步删除undolog
2.分布式事务操作失败,TM向TC发送回滚请求,RM 收到协调器TC发来的回滚请求,通过 XID 和 Branch ID 找到相应的回滚日志记录,通过回滚记录生成反向的更新 SQL 并执行,以完成分支的回滚。 -
优势
应用层基于SQL解析实现了自动补偿,从而最大程度的降低业务侵入性;
将分布式事务中TC(事务协调者)独立部署,负责事务的注册、回滚;
通过全局锁实现了写隔离与读隔离。
5. Seata搭建
下载seata1.3版本
修改file.conf
修改register.conf
找到模板案例,照着抄
案例需要vpn
复制数据库创建语句自己创建出来,作为保存相关必要的数据
csdn不让上传文件,我将所有需要的文件打包放入网盘
这里的两个文件也在里面
运行刚才放入的文件
注意nacos这里默认是开启的
sh nacos-config.sh -h 192.168.14.3 -p 8848 -g SEATA_GROUP
可在windows客户端中查看相应刚刚配置的文件
搭建的nacos的ip加上端口号,必须加上/nacos 账号密码均为nacos
需要修改
在刚刚打开的网页中找到并修改记得一定要修改时区
-
url
jdbc:mysql://192.168.14.53:3306/seata——global?useUnicode=true&serverTimezone=UTC
username,和password改为自己的
启动服务
使用这个命令记得改java的环境变量为java8.
bin/seata-server.sh -p 8091 -n 1
bin/seata-server.sh -p 8092 -n 2
6. Seata Client快速开始
6.1 声明式事务实现(@GlobalTransactional)
接入微服务应用
业务场景:
用户下单,整个业务逻辑由两个微服务构成:
- 订单服务:根据采购需求创建订单。
- 库存服务:对给定的商品扣除库存数量。
6.2 添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
6.3 各微服务对应配置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,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
6.4 测试
- 订单
调用方法后 通过openfeign调用 stock-app的stock接口 记得检查是否注入
@RestController
public class OrderController {
@Autowired
OrderItemDao orderItemDao;
@Autowired
OrderDao orderDao;
@Autowired
StockClient stockClient;
@PostMapping("/add")
@GlobalTransactional(name = "addOrder")
public String add(@RequestBody Ordering ordering){
String yy = DateUtil.format(DateUtil.date(), "YYYYMMDDHHmmssSSS");
Long aLong = Long.valueOf(yy);
ordering.setId(aLong);
orderDao.addOrderMaster(ordering);
List<OrderingProduct> products = ordering.getProducts();
for(OrderingProduct product : products){
product.setOrderId(aLong);
orderItemDao.addOrderItem(product);
stockClient.upQty(product.getProductId(),product.getQty());
}
return "yes";
}
}
这是调用的方法
@GetMapping("/stock")
@GlobalTransactional(name = "addOrder")
public String upQty(Integer productId,Integer qty){
int a = 5/0;
inventoryDao.updateQty(qty,productId);
inventoryLogDao.log("产品ID为" + productId + "减少数量" + qty);
return null;
}
6.6 只需要一个公共注解代替原始注解
@GlobalTransactional(name = “addOrder”)
俩个一定要有相同的名字,可以共同成功,共同失败