文章目录
- 背景
- 一、Seata是什么?
- 二、使用步骤
- 1.依赖版本
- 2.下载Seata
- 2.安装部署Seata
- 3.使用
- 1.创建undo_log表
- 2.添加依赖
- 3.修改配置
- 4.创建拦截器传递XID
- 5.使用@GlobalTransactional注解启动全局事务
背景
多个微服务之间使用FeignClient相互调用,无法保证在同一个事务中执行,当出现异常时,无法回滚。例如在一个方法里先后调用了serviceA.testA()方法和serviceB.testB()方法,此时调用testA成功了,但是调用testB时,因为serviceB服务正在重启发布,调用出现异常,这时无法使testA回滚。
一、Seata是什么?
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
二、使用步骤
1.依赖版本
- SpringBoot 2.3.0.RELEASE
- SpringCloud Hoxton.SR11
- spring-cloud-starter-feign 1.4.7.RELEASE
- seata-spring-boot-starter 1.4.2
2.下载Seata
本文使用的是1.4.2版本
2.安装部署Seata
下面是本文采用的部署方式。
- 将下载好的安装包seata-server-1.4.2.tar.gz解压。
- 进入conf文件夹分别修改registry.conf和file.conf。
- 修改registry.conf文件中的type为eureka,eureka对应的serviceUrl和Seata注册在eureka上的名称,这里直接取名seata。
- 修改file.conf文件中存储方式,这里使用的是默认的file,所以不用修改。如果使用db存储则需要修改数据源、数据库地址、用户名、密码以及在数据库中生成global_table、branch_table、lock_table三个表。
- 进入bin目录执行seata-server.sh启动Seata。
3.使用
1.创建undo_log表
每个需要用到seata的微服务,需要在本地创建一个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,
`ext` VARCHAR(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8;
2.添加依赖
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
3.修改配置
## seata
## 指定微服务所在的分布式事务组
seata.tx-service-group=my_test_tx_group
## seata注册在eureka上的名称,这里seata.service.vgroup-mapping.xxx的xxx为seata.tx-service-group的值
seata.service.vgroup-mapping.my_test_tx_group=seata
## 注册中心类型
seata.registry.type=eureka
seata.registry.eureka.service-url=http://xxxx:7108/eureka/
seata.registry.eureka.application=seata
## 设置undo_log的表名,因为我不是默认的名称所以需要另外指定
seata.client.undo.log-table=tb_undo_log
4.创建拦截器传递XID
Seata 全局事务的传播机制就是指事务上下文的传播,根本上,就是 XID 的应用运行时的传播方式。
默认的,RootContext 的实现是基于 ThreadLocal 的,即 XID 绑定在当前线程上下文中。所以服务内部的 XID 传播通常是天然的通过同一个线程的调用链路串连起来的。默认不做任何处理,事务的上下文就是传播下去的。所以本地方法相互调用都是在同一个XID下的,也就能保证全局事务。但是如果微服务之间调用,要想在同一个全局事务下,则需要将XID传递到其他的为服务中。这样就能保证多个微服务的调用在同一个全局事务下,否则每个微服务都创建一个自己的XID,则都是在自己的全局事务中,就和传统的本地事务一样了。
package com.test.interceptor;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import io.seata.core.context.RootContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;
@Slf4j
@Configuration
public class FeignInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
String xid = RootContext.getXID();
if (StringUtils.isNotBlank(xid)) {
log.info("feign xid:"+xid);
requestTemplate.header(RootContext.KEY_XID, xid);
}
}
}
5.使用@GlobalTransactional注解启动全局事务
@Override
@GlobalTransactional
public void test() {
aService.testA();
int i = 1/0;
bService.testB();
}
当testA执行成功后,int i = 1/0;抛出异常,testA回滚。