架构依赖包版本说明
- Spring Cloud Hoxton.SR10
- Spring cloud alibaba 2.2.1.RELEASE
- Spring boot 2.3.8.RELEASE
- Mybatis-plus 3.4.1
- mysql-connector-java 8.0.17
- Nacos 1.4.1
- Seata 1.4.1
集成步骤
1. 下载,启动Nacos,此处省略
2. 下载,配置,并启动 seata
- 点击进入:https://github.com/seata/seata/releases
- 编辑registry.conf,关键配置如下
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
loadBalance = "RandomLoadBalance"
loadBalanceVirtualNodes = 10
nacos {
application = "seata-server"
serverAddr = "127.0.0.1:8848"
group = "SEATA_GROUP"
namespace = ""
cluster = "default"
username = ""
password = ""
}
}
store {
## store mode: file、db、redis
mode = "db"
## database store property
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
datasource = "druid"
## mysql/oracle/postgresql/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.cj.jdbc.Driver"
url = "jdbc:mysql://xxxx:3306/seata_db"
user = "xxx"
password = "xxx"
minConn = 5
maxConn = 100
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
}
- 启动seata:.\seata-server.bat,端口默认占用 8091
3. 集成到服务消费者
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.4.0-BETA-3</version>
</dependency>
- 拷贝服务端的registry.conf到工程下的resources目录中,内容无需修改
- 创建file.conf到resources目录下,官网有提供,可以下一个模板过来
- 修改file.conf内容
service {
disableGlobalTransaction = false
vgroupMapping.stdv6_tx_group = "default"
enableDegrade = false
#disable seata
disableGlobalTransaction = false
max.commit.retry.timeout = "-1"
max.rollback.retry.timeout = "-1"
}
# seata相关配置
seata:
tx-service-group: stdv6_tx_group
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
group: "SEATA_GROUP"
- 看到以上日志,证明连接seata-server成功
4. 服务提供者
- 配置与消费者一致,不再赘述
- 还有,两个库中记得把undo_log表创建下,结构见官网
5. 编写feign接口,测试分布式事务
- 大概逻辑为A服务调B服务Feign接口
- B服务中对数据库数据进行写入
- A服务调用完B服务后,对自身数据库表进行数据插入
- 逻辑执行完成
- 此时可以测试下,当在A中的Service上加@Translation注解,并模拟A调用完B后进行报错
- 出现的现象是,B服务插入的数据不会回滚
6. 启用分布式事务处理机制
- 在消费者Service方法上,加入@GlobalTransactional注解
- 测试效果,模拟运行时异常,发现本地事务可以正常回滚
- 打印:Branch Rollbacked result: PhaseTwo_Rollbacked 日志
- 但服务提供者数据正常插入了数据库,并未有相关回滚日志,问题在哪里?
7. 原因有2(手动配置数据源,向下传递xid)
- seata介入全局事务提交与回滚,其必须要代理我们本地的数据源
- 根据官方提供的一些示例可知,代理的bean名字为dataSource,所以编写以下代码
/**
* @author qinchen
* @date 2021/5/7 10:45
* @description 手动配置数据源
*/
@Configuration
public class SeataDataSourceConfig {
private final DataSourceProperties dataSourceProperties;
public SeataDataSourceConfig(DataSourceProperties dataSourceProperties) {
this.dataSourceProperties = dataSourceProperties;
}
@Bean
@Primary
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(dataSourceProperties.getUrl());
dataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
dataSource.setUsername(dataSourceProperties.getUsername());
dataSource.setPassword(dataSourceProperties.getPassword());
return dataSource;
}
}
- 配置了数据源后,发现依然不行,结果原因是使用Feign接口调用时,全局事务id默认并不会自动向下传递
- 编写一个SeataFeignClientInterceptor,并让其加入到容器中
/**
* @author qinchen
* @date 2021/5/7 10:56
* @description 将全局事务ID向下传递
*/
@Slf4j
public class SeataFeignClientInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// 将全局事务ID向下传递
String xid = RootContext.getXID();
if (StringUtils.isNotBlank(xid)) {
log.info("将全局事务xid = {} 向下传递", xid);
template.header(RootContext.KEY_XID, xid);
}
}
}
@Slf4j
@Configuration
public class ApplicationAutoConfiguration {
@Bean
public RequestInterceptor requestInterceptor(){
log.info("实例化拦截器:SeataFeignClientInterceptor,纳入到Bean容器");
return new SeataFeignClientInterceptor();
}
}