赞
踩
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
全局事务:全局事务指的是一次性操作多个资源管理器完成的事务,由一组分支事务(本地事务)组成。
分支事务(本地事务):本地事务由本地资源管理器(通常指数据库管理系统 DBMS,例如 MySQL、Oracle 等)管理,严格地支持 ACID 特性,高效可靠。本地事务不具备分布式事务的处理能力,隔离的最小单位受限于资源管理器,即本地事务只能对自己数据库的操作进行控制,对于其他数据库的操作则无能为力。
工作流程
Seata 对分布式事务的协调和控制,主要是通过 XID 和 3 个核心组件实现的。
XID
核心组件
Seata 定义了 3 个核心组件:
TC(Transaction Coordinator):事务协调器,直接调度事务参与者RM。负责将RM的反馈结果响应给TM,并听从TM的最终决议,将具体决议(提交或回滚)发送给RM执行。相当于中间人,主要负责维护全局事务和分支事务的状态。
TM(Transaction Manager):事务管理器,它是事务的发起者(具体的微服务)。根据RM第一阶段的执行结果,进行决议。并将决议反馈给TC。相当于发号施令的
RM(Resource Manager):资源管理器,其实就是事务的参与者。获取TC的执行命令具去执行分支事务的第一阶段以及第二阶段,并将执行结果反馈给TC,相当于具体做事的
以上三个组件相互协作,TC 以 Seata 服务器(Server)形式独立部署,TM 和 RM 则是以 Seata Client 的形式集成在微服务中运行。
Seata 的整体工作流程如下:
注:本文使用Mysql为例
需要使用Nacos 教程:https://gaohuanjie.blog.csdn.net/article/details/135294828
需要git Bash窗口
官方下载地址: https://www.git-scm.com/download/
git bash下载安装就行,如果不会提供一个教程 https://blog.csdn.net/qq_33204709/article/details/133963278
官网下载地址:https://seata.io/zh-cn/blog/download.html
下载完成后,将文件解压到任意地方 (不要忘记解压的地方)
接下来的大部分操作都在下图的根目录展开
新建数据库seata,然后在解压的seata文件找到script->server->db->mysql.sql,执行这个sql脚本。
config.txt
文件在seata/script/config-center
目录下,需要修改的地方如下:
首先在nacos控制台添加新的命名空间
新建命名空间
空间ID可以不填,但是自动生成之后需要记住,接下来配置需要很重要
命名空间和描述可以任意但是最好写seata见名知意
进入seata目录,找到nacos-config.sh,路径为:script->config-center->nacos->nacos-config.sh
进入git bash窗口,执行以下命令:
sh nacos-config.sh -h nacos服务地址 -p 8848 -g SEATA_GROUP -t 刚刚注册时候的命名空间id -u nacos -w nacos
案例
sh nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP -t cc7b3c50-d6e9-4b86-934a-1cd7e5a6a0e2 -u nacos -w nacos
参数详解:
注意:-u 和 -w 后面是Nacos的登录账户和密码,最好不要修改,除非你能记住,修改后下面配置文件的nacos账号密码就要修改为对应的
执行完上述命令之后 配置数应该为103,可能不一样和(图为107是因为后续需要根据你的微服务数量添加对应的配置,后续说明)
进入seata/conf目录下,有两个配置文件,把application.yml 随意修改一个名字,然后把 application.example.yml修改成application.yml 作为主要配置文件。
修改application.yml文件,这里使用的nacos作为注册中心,所以需要修改的地方有:
第一步将原来application.yml 文件中的console和security拷贝走,复制到主要配置文件(最初的application.example.yml)文件中
console放在这个位置
security放在最下面,注意缩进格式
然后开始修改主要配置文件(最初的application.example.yml)中的设置
首先修改seata
修改seata下的registry
修改seata下的store
至此文件就修改完成了
找到seata-server.bat,点击启动,路径为:seata->bin->seata-server.bat,成功后可以在nacos控制台服务列表中看到多了一个服务。
如果闪退了说明有错误,需要在该目录下打开cmd控制台输入seata-server.bat 启动,错误是什么
常见问题
部署成功后就会有seata-server的服务名的服务(其他服务是分布式服务,后续说明)
注意所有的服务模块都需要该操作,在此只演示一个,但是所以服务都需要这些操作
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
第一步 在该页面创建配置
信息设置为这样,Data ID格式为service.vgroupMapping.XXX XXX可以任意起名,但是之后需要和properties中seata.tx-service-group和seata.service.vgroup-mapping.XXX 保持一致
Group改为SEATA_GROUP (Group是上文中设置为SEATA_GROUP的)
配置内容设置为Default
seata.enabled=true # 服务id,不重复即可 seata.application-id=wallet seata.enable-auto-data-source-proxy=false # group名,需要跟Nacos添加的配置保持一致 seata.tx-service-group=service-wallet-group # seata.service.vgroup-mapping.XXX XXX需要跟上面保持一致 seata.service.vgroup-mapping.service-wallet-group=default seata.service.disable-global-transaction=false # seata.registry的信息,跟application.yml的registry信息保持一致 seata.registry.type=nacos seata.registry.nacos.application=seata-server seata.registry.nacos.server-addr=127.0.0.1:8848 seata.registry.nacos.group=SEATA_GROUP seata.registry.nacos.namespace=cc7b3c50-d6e9-4b86-934a-1cd7e5a6a0e2 seata.registry.nacos.username=nacos seata.registry.nacos.password=nacos seata.registry.nacos.cluster=default # seata.config的信息,跟application.yml的config信息保持一致 seata.config.type=nacos seata.config.nacos.server-addr=127.0.0.1:8848 seata.config.nacos.group=SEATA_GROUP seata.config.nacos.namespace=cc7b3c50-d6e9-4b86-934a-1cd7e5a6a0e2 seata.config.nacos.username=nacos seata.config.nacos.password=nacos seata.client.rm.report-success-enable=true seata.client.rm.report-retry-count=5
该步不需要每个都配置,只需要在主要的方法上添加开启事务即可
// name任意,不重复即可 rollbackFor最好设置为Exception.class,不然有些异常不回滚 @GlobalTransactional(name = "order",rollbackFor = Exception.class) // 案例 /** * * @param userId 用户id 目前只有1 * @param productId 商品id 目前有1-4 * @param quantity 购买数量 * @param addressId 地址信息id * @return */ @GetMapping("/order/add.do") @GlobalTransactional(name = "order",rollbackFor = Exception.class) public Map<String,Object> add(Long userId, Long productId, Integer quantity, Long addressId) { System.out.println("事务id是"+ RootContext.getXID()); Order order = new Order(); order.setUserId(userId); //通过productId获取商品信息 Product product = IProductService.get(productId); order.setProductId(product.getId()); order.setProductName(product.getName());//商品名称在后期可能发生变化,这里记录的是下单时商品名称 order.setProductPrice(product.getPrice());//商品名称在后期可能发生变化,这里记录的是下单时商品名称 //通过addressId获取地址信息 Address address = IAddressService.get(addressId); order.setRealName(address.getRealName());//收件人姓名在后期可能发生变化,这里记录的是下单时用户选中的邮寄地址对应的收件人姓名 order.setMobile(address.getMobile());//收件人手机号在后期可能发生变化,这里记录的是下单时用户选中的邮寄地址对应的收件人手机号 order.setAddressDetail(address.getDetail());//收件人详细地址在后期可能发生变化,这里记录的是下单时用户选中的邮寄地址对应的收件人详细地址 order.setQuantity(quantity); double amount = product.getPrice() * quantity;//计算商品总额 Map<String,Object> map = new HashMap<>(); if(IProductService.update(addressId,quantity) //修改商品库存 && IWalletService.getWallet(userId,amount) //修改钱包余额 && orderService.save(order)){//添加订单信息 map.put("state","200"); map.put("message","订单创建成功!"); }else{ map.put("state","100"); map.put("message","订单创建失败!"); } return map;
添加@EnableAutoDataSourceProxy
// 案例
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
@EnableAutoDataSourceProxy
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
至此就已经将seata事务管理添加进入项目了
添加@GlobalTransactional注解日志打印,显示已经回滚
正常执行的子服务
异常的子服务
xxx.undo_log不存在
java.sql.SQLSyntaxErrorException: Table ‘mall.undo_log’ doesn’t exist
在服务操作的目标数据库执行下SQL添加表
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (
`id` bigint(0) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(0) NOT NULL,
`xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(0) NOT NULL,
`log_created` datetime(0) NOT NULL,
`log_modified` datetime(0) NOT NULL,
`ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
事务不回滚
在添加@GlobalTransactional注解的服务中,日志显示status:RollBacked但是在其他服务模块中日志打印的是Commited
该问题一般是因为XID不一致导致的,一般不会出现该问题本文没有遇到该问题
提供一个方法来判断是否是XID不一致或者子服务中XID为null
// RootContext.getXID()就是获取事务的XID 在子服务和总服务下输出一下就能知道是否一致
System.out.println("事务id是"+ RootContext.getXID());
第一种情况
如果使用的不是本文的pring-cloud-starter-alibaba-seata这个依赖,而是使用的这以下依赖有可能出现该问题
<!--seata1.7.0--> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.7.0</version> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>1.7.0</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <version>2022.0.0.0-RC2</version> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> </exclusion> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> </exclusion> </exclusions> </dependency>
第二种情况
seata 分布式事务没有传递xid导致事务失效
本文没有出现这种情况,是第一种情况所以在此提供的一种解决方案
https://blog.csdn.net/qq_45278455/article/details/124364505
安装和部署参考了该文章,并在此基础上改进
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。