十二、集成分布式事务组件Seata
什么是Seata
网址:seata.io
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
seata术语
TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。相当于下载的seata
TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。相当于应用(business、member)
RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。相当于数据库。
Seata分布式事务的原理
正常情况下,如果某一步失败了,需要做补偿来还原。补偿交给seata来做。
Seata分布式事务的四种模式
- AT模式,默认,简单,需要增加undo_log表,生成反向SQL,性能高。(对于insert,就反向生成delete). 回滚后,原来没数据的, 现在还是没有数据.
- TCC模式,try confirm/cancel,三个阶段的代码都得自己实现,Seata只负责调度。confirm和cancel只能实现一次。对业务代码侵入性较强,必要时可能要修改数据库。
- SAGA模式,长事务解决方案,需要程序员自己编写两阶段代码(AT模式不需要写第二阶段)。基于状态机实现,需要一个JSON文件,可异步执行。
- XA模式,XA协议是由X/Open组织提出的分布式事务处理规范,基于数据库的XA协议来实现2PC又称为XA方案,适用于强一致性的场景,比如金融、银行等。
Seata的实际使用
哪一个功能需要seata
选中座位后事务处理:
座位表修改售卖情况sell;
余票详情表修改余票;
为会员增加购票记录
更新确认订单为成功
橙色部分需要调用business的数据库,黑色的调用member的数据库。
回滚只能回滚本端的,不能回滚远端调用的。
首先要把ticket表中的col字段更改为seat_col字段,因为col是关键字,seata的AT模式会自动生成反向sql,且没有反引号' ',所以要求表里不能有关键字。
集成seata
创建undo_log表
1 CREATE TABLE `undo_log` ( 2 `id` bigint(20) NOT NULL AUTO_INCREMENT, 3 `branch_id` bigint(20) NOT NULL, 4 `xid` varchar(100) NOT NULL, 5 `context` varchar(128) NOT NULL, 6 `rollback_info` longblob NOT NULL, 7 `log_status` int(11) NOT NULL, 8 `log_created` datetime NOT NULL, 9 `log_modified` datetime NOT NULL, 10 `ext` varchar(100) DEFAULT NULL, 11 PRIMARY KEY (`id`), 12 UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) 13 ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
下载的seata直接可以解压运行。
通用配置引入依赖
1 <dependency> 2 <groupId>com.alibaba.cloud</groupId> 3 <artifactId>spring-cloud-starter-alibaba-seata</artifactId> 4 </dependency>
配置(可以不写配置,因为有默认值)。
1 seata: 2 # 事务组名称 3 tx-service-group: train-group 4 # 事务组和seata集群做关联 5 service: 6 vgroup-mapping: 7 train-group: default 8 grouplist: 9 # 事务组对应的机器 10 default: 127.0.0.1:8091
正常注解为@TranSaction,分布式事务注解@GlobalTranSaction
处于同一分布式事务的id RootContext.getXID() 应该相同。
统一异常处理中
1 @ExceptionHandler(value = Exception.class) 2 @ResponseBody 3 public CommonResp exceptionHandler(Exception e) throws Exception { 4 LOG.info("seata全局事务ID: {}", RootContext.getXID()); 5 // 如果是在一次全局事务里出异常了,就不要包装返回值,将异常抛给调用方,让调用方回滚事务 6 if (StrUtil.isNotBlank(RootContext.getXID())) { 7 throw e; 8 } 9 CommonResp commonResp = new CommonResp(); 10 LOG.error("系统异常:", e); 11 commonResp.setSuccess(false); 12 commonResp.setMessage("系统出现异常,请联系管理员"); 13 //commonResp.setMessage(e.getMessage()); 14 return commonResp; 15 }
上面如果不加if判断的话,member出现异常时,虽然commonResp.success=false,但是接口返回码是200,business会认为调用是成功的。
步骤!!!
1、增加一个undo_log表,格式固定,所有需要分布式事务的都需要添加
2、引入依赖并配置
3、@GlobalTransactional 开启事务
案例:两个人买一张票
Seata Server配置Nacos
客户端和配置中心整合
1 seata: 2 # seata注册中心 3 registry: 4 type: nacos 5 nacos: 6 application: seata-server 7 server-addr: 127.0.0.1:8848 8 group: SEATA_GROUP 9 namespace: train 10 username: nacos 11 password: nacos 12 # seata配置中心 13 config: 14 type: nacos 15 nacos: 16 server-addr: 127.0.0.1:8848 17 group: SEATA_GROUP 18 namespace: train 19 data-id: seataServer.properties 20 username: nacos 21 password: nacos 22 # 事务组名称,必须在nacos中有配置过:service.vgroupMapping.train-group=default 23 tx-service-group: train-group 24 # 事务组和seata集群做关联 25 #service: 26 # vgroup-mapping: 27 # test-group: default 28 # seata集群对应的机器 29 #grouplist: 30 # default: 127.0.0.1:8091 31 # 以下是nacos中的seataServer.properties的相关配置 32 # 和微服务模块的seata.tx-service-group保持一致
异常三次回滚
步骤:每个事务一开始往global_table插入一条数据,多少个数据库操作就写多少条数据到branch_table。事务结束清空branch,回滚就读branch,生成反向sql,结束也要清空branch。
seata事务只支持INSERT、UPDATE、DELETE三类DML语法的部分功能。
使用限制:
- 不支持SQL嵌套
- 不支持多表复杂SQL
- 不支持存储过程、触发器
- 部分数据库不支持批量更新