若依框架集成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服务也回滚了。

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

  • 11
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RuoYi-plus是一个基于RuoYi升级的springcloud版本管理系统。软件由SMP多商户后台管理系统+API后端接口组成,项目支持cms内容管理和商城业务,提供单机版服务、微服务体系双向选择,微服务集成分布式事务解决方案seata,框架基于SpringBoot2.x,springcloudG版本。 RuoYi-plus项目介绍 项目核心: 多商户权限系统+商城平台+CMS内容管理+微服务API系统 架构技术: 基于SpringBoot2.x, springcloud G版本eureka、hystrix、feign、config、gateway微服务架构体系的全新版本,集成分布式事务解决方案seata集成redis、quartz、tk.mybatis、lombok、各种设计模式等。 项目优势: 架构更清晰、技术更前沿、代码更整洁、页面更美观、学习商用均可。可用于OA系统、 CRM系统、 PDM系统等二次开发,微服务架构学习,非常适用于公司管理平台旧版本升级、新平台搭建快速整合、同时提供API接口服务(供APP、微端、h5等使用)。 RuoYi-plus内置功能 管理平台基础功能 1、用户管理:用户是系统操作者,该功能主要完成系统用户配置。 2、部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。 3、岗位管理:配置系统用户所属担任职务。 4、菜单管理:配置系统菜单,操作权限,按钮权限标识等。 5、角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。 6、字典管理:对系统中经常使用的一些较为固定的数据进行维护。 7、参数管理:对系统动态配置常用参数。 8、通知公告:系统通知公告信息发布维护。 9、操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 10、登录日志:系统登录日志记录查询包含登录异常。 11、在线用户:当前系统中活跃用户状态监控。 12、定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。 13、代码生成:前后端代码的生成(java、html、xml、sql)支持CRUD下载 。 14、系统接口:根据业务代码自动生成相关的api接口文档。 15、服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。 16、在线构建器:拖动表单元素生成相应的HTML代码。 17、连接池监视:监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。 商城业务基础功能 1、商户管理:多商户系统核心业务,商户管理对应权限数据。 2、类别管理:商品类别配置,为商品区分类别。 3、商品管理:每个商户下的商品信息。 CMS内容基础功能 1、文章类别管理:文章列表信息,为文章区分不同类别。 2、文章信息管理:文章具体信息,直接生成文章html页面。 3、素材类别管理:为素材提供类别以区分不同业务的素材图片/视频。 4、素材信息管理:文章信息的素材图片管理。 API接口基础功能 1、业务模块:接收网关gateWay转发请求,api微服务结构处理具体业务,解决分布式事务。 2、订单模块:订单相关业务处理。 3、商品模块:商品相关业务处理。 商用须知 本项目遵循MIT协议,无需授权,留言备注公司信息即可。 演示地址账号:tecom  密码:123456

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值