这是一段完整的配置spring cloud +feign+nacos 项目下,增加 seata 的 AT 模式作为的分布式事务解决方案,同时希望可以通过这个流程下来,
可以看到我在过程中参考的信息来源,去总结如何引入框架,同时存在疑问信息,从哪些现有信息里找到解决方案的方式有些帮助。
版本信息:
springBoot:2.2.5.RELEASE
nacos:2.0 以上
seata:1.5.2
1、下载和初始化 seata 数据库
(1)下载
访问:
Releases · seata/seata · GitHub
下载找到Assets下的 seata-server 压缩包。
(2)初始化所需的 Mysql 数据库
此处示例用的是 mysql,还可以支持 oracle 和 postgresql
创建一个DB,此处命名为 db_seata,进入库中
执行解压缩文件中 seat/script/server/db/mysql.sql 的 SQL 语句
(3)为业务服务对应数据库添加 undo_log
这里有熟悉的,是不是很像 mysql 的undo_log 文件机制,seata 的实现其实也是模拟 mysql 的 undo_log 的实现方案去做到回滚的。
但是目前 seata 回滚的 sql 还是存在一些限制,详情可查阅:SQL限制
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、修改启动的配置文件
seata/config/application.yml
参考的配置方式,来源于同级目录下 application.example.yml
里面针对各个类型的配置示例,都提供出来,包括以下的三点,
既然用到 nacos 和 mysql,那么配置中心、注册中心、store 干脆都使用上。
(1)修改配置中心
seata: config: type: nacos nacos: server-addr: 127.0.0.1:8848 group : "SEATA_GROUP" namespace: "" dataId: "seataServer.properties" username: "nacos" password: "nacos"
参考:nacos
(2)修改注册中心
registry: type: nacos nacos: ##注册的服务名称 application: seata-server ##nacos的地址端口 server-addr: 127.0.0.1:8848 ##此处注意自己的业务服务的 group 和 namespace 配置,匹配得到才能访问,这里仅仅举例 group : "SEATA_GROUP" namespace: "" #对应的账号密码 username: "nacos" password: "nacos"
(3)修改 DB 连接到 mysql
store:
# support: file 、 db 、 redis mode: db db: datasource: druid db-type: mysql driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true user: mysql password: mysql min-conn: 5 max-conn: 100 global-table: global_table branch-table: branch_table lock-table: lock_table distributed-lock-table: distributed_lock query-limit: 100 max-wait: 5000
大致图样:
2、上传配置文件到 Nacos 的配置中心。
参考官方文档:nacos
进入目录 seata/script/config-center
(1)修改配置文件
修改 config.txt 里面的mode 为 db 数据库,并调整链接
(2)上传到nacos
方式一:
sh ${SEATAPATH}/script/config-center/nacos/nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -t 5a3c7d6c-f497-4d68-a71a-2e5e3340b3ca -u username -w password
//localhost -p 8848 --> nacos 的地址
//-g SEATA_GROUP -t 5a3c7d6c-f497-4d68-a71a-2e5e3340b3ca ---> 对应前面配置 nacos 的注册中心的 group 和 namespace,namespace 需要用业务服务的,若用的 public ,-t 这部分可删除
// -u username -w password -->username:nacos 账号,password:nacos 密码
sh ./script/config-center/nacos/nacos-config.sh -h 127.0.0.1 -p 8848 -g SEATA_GROUP -t 5a3c7d6c-f497-4d68-a71a-2e5e3340b3ca -u nacos -w nacos
(seata 和业务服务均需要在这个初始化完成后再启动)
方式二:
在对应nacos 创建一个 seataServer.properties 将配置信息,黏贴进去。
3、业务服务的配置
(1)添加依赖
prarent pom 添加依赖
io.seata seata-spring-boot-starter1.5.2 io.seata seata-allio.seata seata-all1.5.2
依赖关联回滚的服务添加依赖
io.seata seata-spring-boot-starter1.5.2
(2)业务服务 application 文件修改
##这里和前方配置 seata 的配置文件是一致的
seata: config: # support: nacos, consul, apollo, zk, etcd3 type: nacos nacos: server-addr: 127.0.0.1:8848 group: "SEATA_GROUP" dataId: "seataServer.properties" username: "nacos" password: "nacos" registry: type: nacos nacos: ##注册的服务名称 application: seata-server ##nacos的地址端口 server-addr: 127.0.0.1:8848 ##此处注意自己的业务服务的 group 和 namespace 配置,匹配得到才能访问,这里仅仅举例 group : "SEATA_GROUP" ##对应的账号密码 username: "nacos" password: "nacos"
若是配置为 dataId: "seataServer.properties",那么配置文件走的是这里,seata 的 application.yal 关于 mode 、store、决定生效取决于配置文件
(3)feign 的全局 xid 传输
通过全局配置,增加服务通信信息中,可以携带 seata 的 xid 进行传输
@Configuration
public class FeignSupportConfig {
@Bean
public RequestInterceptor requestInterceptor(){
return new FeignBasicAuthRequestInterceptor();
}
}
@Slf4j
public class FeignBasicAuthRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
requestTemplate.header(AsapConstants.DATA_SOURCE_CODE, AsapConstants.DATA_SOURCE);
requestTemplate.header(AsapConstants.REQUEST_ID, TraceIdUtils.getTraceId());
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (ObjectUtils.isEmpty(requestAttributes)){
return ;
}
ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
HttpServletRequest request = attributes.getRequest();
Enumeration headerNames = request.getHeaderNames();
//服务通信携带初始请求的 header
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
// 跳过 content-length
if ("content-length".equals(name)){
continue;
}
requestTemplate.header(name, values);
}
//传递 seata 回滚 xid
String xid = RootContext.getXID();
requestTemplate.header(RootContext.KEY_XID, xid);
} else {
log.info("feign interceptor error header:{}", requestTemplate);
}
}
写 feign 通信接口时,添加配置文件依赖。
4、启动服务
seata-server.bat -p 8091 -h 127.0.0.1
启动成功后观察,启动的 seata web 服务时 7091 端口。 访问127.0.0.1:7091
业务服务启动后,结合 @GlobalTransactional 注解验证测试有无效果。
5、常见问题解决方案
Seata常见问题
6、遇到的问题
(1)服务部署过程中,seataServer.properties 的生效优先于seata的 config 文件application.yml ,过程中修改数据库的信息,仅仅修改这里不生效。
(2)由于项目中存在全局异常的处理,所以导致明明抛出异常但是事务还是不会回滚。
看 seata 的代码,response 的状态码异常才会 触发回滚。
7、常见 seata事务回滚失败的情况
(1)xid 在服务中传输存在问题,打日志检查,处理方式见第 3(2)。
(2)feign 的 db 没脸上,事务范围内,执行过程中在执行 sql 后打上 断点,然后登录seata 的管理台,检查是否存在事务和全局锁信息,有刚刚执行 Sql 的锁信息。
(3)全局异常导致失效,可以通过全局异常捕获过滤这些接口,或是通过手动执行事务的方式触发回滚。
try {
TransactionManager manager = TransactionManagerHolder.get();
manager.rollback(RootContext.getXID());
}catch (Exception e){
log.error("手动回滚全局异常失败:{}",e);
}



