若依框架集成seata分布式事务的一些幺蛾子

一、bug连环炮

A服务调用B服务,B服务异常,A服务插入的数据没有回退,前面没有思路,就查了下,说是没有切换为seata的数据源,我就在启动类加了一个@EnableAutoDataSourceProxy注解,结果就开始报错了:

2024-03-19 10:49:30.653 [http-nio-8080-exec-2] INFO  c.a.n.client.config.impl.CacheData - Line:180 [CacheData.java:180] - [fixed-dev-10.10.3.31_20046] [add-listener] ok, tenant=dev, dataId=client.rm.lock.retryTimes, group=SEATA_GROUP, cnt=2
2024-03-19 10:49:30.952 [http-nio-8080-exec-2] WARN  i.s.c.l.EnhancedServiceLoader$InnerEnhancedServiceLoader - Line:543 [EnhancedServiceLoader.java:543] - Load [io.seata.rm.datasource.undo.parser.ProtostuffUndoLogParser] class fail. io/protostuff/runtime/IdStrategy
2024-03-19 10:49:31.046 [http-nio-8080-exec-2] ERROR i.s.rm.datasource.ConnectionProxy - Line:258 [ConnectionProxy.java:258] - process connectionProxy commit error: ERROR: column "id" of relation "undo_log" does not exist
  位置:23
org.postgresql.util.PSQLException: ERROR: column "id" of relation "undo_log" does not exist
  位置:23
	at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2553)
	at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2285)
	at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:323)
	at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:481)
	at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:401)
	at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:164)
	at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:130)
	at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_executeUpdate(FilterChainImpl.java:3238)
	at com.alibaba.druid.filter.FilterAdapter.preparedStatement_executeUpdate(FilterAdapter.java:1075)
	at com.alibaba.druid.filter.FilterEventAdapter.preparedStatement_executeUpdate(FilterEventAdapter.java:486)
	at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_executeUpdate(FilterChainImpl.java:3236)
	at com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl.executeUpdate(PreparedStatementProxyImpl.java:179)
	at com.alibaba.druid.pool.DruidPooledPreparedStatement.executeUpdate(DruidPooledPreparedStatement.java:241)
	at io.seata.rm.datasource.PreparedStatementProxy.lambda$executeUpdate$2(PreparedStatementProxy.java:65)
	at io.seata.rm.datasource.exec.AbstractDMLBaseExecutor.executeAutoCommitFalse(AbstractDMLBaseExecutor.java:100)

给业务库的undo_log表增加Id字段:

ALTER TABLE public.undo_log   ADD COLUMN id INT NULL;
2024-03-19 11:09:34.382 [http-nio-8080-exec-6] ERROR i.s.rm.datasource.ConnectionProxy - Line:258 [ConnectionProxy.java:258] - process connectionProxy commit error: ERROR: relation "undo_log_id_seq" does not exist
  位置:120
org.postgresql.util.PSQLException: ERROR: relation "undo_log_id_seq" does not exist
  位置:120
	at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2553)
	at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2285)
	at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:323)
	at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:481)
	at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:401)
	at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:164)
	at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:130)

给pg的undo_log表增加序列:

CREATE SEQUENCE public.undo_log_id_seq
	INCREMENT BY 1
	MINVALUE 1
	MAXVALUE 9223372036854775807
	START 1
	CACHE 1
	NO CYCLE;
comment on sequence  undo_log_id_seq is 'undolog主键序列';

再次发起请求时seata的控制台报错:

11:23:37.192  INFO --- [nPool.commonPool-worker-3] i.s.s.s.db.lock.LockStoreDataBaseDAO     : Global lock batch acquire failed, xid 10.10.10.74:8091:81566060276966089 branchId 81566060276966121 pks [81566060276966093, 31]
11:23:37.196  INFO --- [nPool.commonPool-worker-3] i.s.c.e.AbstractExceptionHandler         : this request cannot acquire global lock, you can let Seata retry by setting config [client.rm.lock.retryPolicyBranchRollbackOnConflict] = false or manually retry by yourself. request: xid=10.10.10.74:8091:81566060276966089,branchType=AT,resourceId=jdbc:postgresql://10.10.3.31:20042/shtl_dev,lockKey=undo_log:81566060276966093;sys_config:31
11:23:37.215  INFO --- [     batchLoggerPrint_1_1] i.s.c.r.p.server.BatchLogHandler         : SeataMergeMessage xid=10.10.10.74:8091:81566060276966089,branchType=AT,resourceId=jdbc:postgresql://10.10.3.31:20042/shtl_dev,lockKey=undo_log:81566060276966093;sys_config:31
,clientIp:10.10.10.74,vgroup:default_tx_group
11:23:37.229 ERROR --- [nPool.commonPool-worker-3] i.s.s.s.db.lock.LockStoreDataBaseDAO     : Global lock batch acquire error: Batch entry 1 insert into lock_table(xid, transaction_id, branch_id, resource_id, table_name, pk, row_key, gmt_create, gmt_modified,status) values ('10.10.10.74:8091:81566060276966089', 81566060276966089, 81566060276966123, 'jdbc:postgresql://10.10.3.31:20042/shtl_dev', 'sys_config', '31', 'jdbc:postgresql://10.10.3.31:20042/shtl_dev^^^sys_config^^^31', now(), now(), 0) was aborted: ERROR: duplicate key value violates unique constraint "pk_lock_table"
  详细:Key (row_key)=(jdbc:postgresql://10.10.3.31:20042/shtl_dev^^^sys_config^^^31) already exists.  Call getNextException to see other errors in the batch.
==>
java.sql.BatchUpdateException: Batch entry 1 insert into lock_table(xid, transaction_id, branch_id, resource_id, table_name, pk, row_key, gmt_create, gmt_modified,status) values ('10.10.10.74:8091:81566060276966089', 81566060276966089, 81566060276966123, 'jdbc:postgresql://10.10.3.31:20042/shtl_dev', 'sys_config', '31', 'jdbc:postgresql://10.10.3.31:20042/shtl_dev^^^sys_config^^^31', now(), now(), 0) was aborted: ERROR: duplicate key value violates unique constraint "pk_lock_table"
  详细:Key (row_key)=(jdbc:postgresql://10.10.3.31:20042/shtl_dev^^^sys_config^^^31) already exists.  Call getNextException to see other errors in the batch.
	at org.postgresql.jdbc.BatchResultHandler.handleError(BatchResultHandler.java:165)
	at org.postgresql.core.ResultHandlerDelegate.handleError(ResultHandlerDelegate.java:52)
	at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2402)
	at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:574)
	at org.postgresql.jdbc.PgStatement.internalExecuteBatch(PgStatement.java:896)
	at org.postgresql.jdbc.PgStatement.executeBatch(PgStatement.java:919)

明明lock_table里面一条数据都没有,为啥会说有重复的数据,很是邪门,后面看别的博客说要改为手动配置数据源,本身这个项目就是用的多数据源模式,是自己手动配置的,多方尝试还是不行,后面看到了博客说可以使用切面的方式来回滚,参考了[微服务 seata 没有回滚的问题]博客
有的博客也有说没有给业务库里的undolog设置主键的问题(加了主键还是不行),也有的说微服务中applicaiton.yml的关闭数据源代理的配置:seata.enable-auto-data-source-proxy: false有问题,需要改成seata.enableAutoDataSourceProxy=false(改了也不行,其实最后证明这两个配置选择其中一个都是对的),也有说是xid在不同服务传递的问题,增加mvc的配置,这些我都加了,还是不行,最后参考了切面的方法,加了之后,undolog,lock_table表都有数据了。
https://seata.apache.org/zh-cn/blog/seata-spring-boot-aop-aspectj/
https://seata.apache.org/zh-cn/docs/overview/faq/

然后seata的控制台一直在重试回滚,但却一直失败:

17:55:51.654  INFO --- [Pool.commonPool-worker-23] io.seata.core.rpc.netty.ChannelManager   : No channel is available for resource[jdbc:postgresql://127.0.0.1:3306/seata_dev] as alternative of saas-ceic:10.10.10.74:54869
17:55:51.654  INFO --- [nPool.commonPool-worker-3] io.seata.core.rpc.netty.ChannelManager   : No channel is available for resource[jdbc:postgresql://127.0.0.1:3306/seata_dev] as alternative of saas-ceic:10.10.10.74:58425
17:55:51.654 ERROR --- [Pool.commonPool-worker-23] io.seata.server.coordinator.DefaultCore  : Rollback branch transaction exception, xid = 10.10.10.74:8091:8800534989775381989 branchId = 8800534989775381994 exception = rm client is not connected. dbkey:jdbc:postgresql://127.0.0.1:3306/seata_dev,clientId:saas-ceic:10.10.10.74:54869

后面就索性把seata服务停了,把lock_table,global_table这几个表的数据删掉。

本想看源码,但实在时间紧迫,也有点像无头苍蝇,但也只能继续看看大佬写的博客,站在巨人的肩膀上说不定能找到解决的办法,不过我的这种方式并不是很可取,所以大家有时间还是从源码来分析和解决问题。当然自己也去打断点看源码,但因为自己静不下心来,而且时间不允许,所以把为了解决这个事务不回滚的前后看的博客也都罗列一下:
1、Seata分布式事务失效,不生效(事务不回滚)的常见场景(切面的写法)
2、SpringCloud+Nacos+Seata分布式事务
(这个模仿着增加了FeignConfig配置,但没啥作用)
SpringCloud+Seata+MybatisPlus多数据源@GlobalTransactional异常数据未回滚事务失效的解决方案
(这个提醒了我要把B服务的异常捕获给删掉)
3、微服务中 Seata “分支事务不回滚”问题的复盘(这个增加了SeataHandlerInterceptor拦截器)
4、seata AT模式中,seata失效,RM(事务参与者) 没有访问undo_log 表,也没有生成undo_log日志记录 ,可是global_table区有日志 ,解决BUG!
(看了这个博客我就一直觉得是我数据源的问题,就多次按照下面这个博客尝试修改数据源)
5、springboot研究十:springboot多数据源整合seata-AT模式(这个结合自己的服务的多数据源也修改了,但修改之后一直提示注入依赖有问题)

当然除了这些博客以外,我还看了很多其他的博客和seata的官网,若依的官网,以及在采用这些博客的解决办法之后,出现的一些bug,就这样折腾了2天还是没解决。话说回来,如果我这两天用来看源码,说不定还没有看完,也解决不了。看前人的经验的好处是快速的知道自己的问题可能在哪里,后面再看[源码]的时候,就会有针对性,当然在看别人写的博客的时候,他们有的也会分析源码,也可以跟着大概看一下哪些源码比较重要,后面自己看就比较有针对性了。

自己感觉源码分析比较重要的点的博客:
Seata 全局锁等待超时 问题排查
seata源码分析(AT)-事务提交和回滚

二、转折点

由于查了2天也没查到原因,所以只能求助同事了,同事在项目上实现过seata分布式事务,所以他是让我修改了微服务中的seata的配置,但我感觉不是配置的问题,因为最后解决了之后,只是因为一个注解的问题。

自己翻看了很多事务不回滚的博客的解决办法,最后基本上能试的都试了(这种方式不可取,有时间还是要看源码排查一下),偶然发现一个博客在a服务添加@GlobalTransactional(name = "test-seata", rollbackFor = Throwable.class) 注解的下面增加了另外一个注解@Transactional,自己也想死马当活马医,就添加了@Transactional后再次调用,发现不像之前一样,即便b服务插入报错了,a服务还是能照常插入进去,这次的结果和预想的一样,a服务插入成功,b服务插入失败,最后a服务也回滚了。

------------------------------知道的越多,不知道的越多------------------------------

你可能感兴趣的:(分布式)