微服务中 Seata “分支事务不回滚”问题的复盘

本篇记录原写于去年。

背景

一个下单逻辑跨了3个服务,采用 Seata AT 模式做分布式事务。

发现问题

分布式事务的处理并未成功,具体表现为:在出现异常后,3 个数据库里的表谁也没回滚。

本来以为是自己看错了,但是经过笔者的多次验证后,得到的结果都是如此,分支事务并未被正常处理。

好的,发现问题后该怎么办呢?

尝试解决问题

“小问题,轻轻松松~”

刚开始看到这个问题,笔者并没有觉得是个大问题,此时我并没有意识到严重性,也可以说是“轻敌”了。

当时,主要觉得问题可能出现配置和整合步骤上,于是做了如下4个事情:

  • 检查整合步骤,是不是漏掉了哪个步骤,或者少了哪些配置,亦或者是哪个配置项因为粗心没配置好。
  • 数据库中的表是否有问题。
  • 项目重启(遇事不决,重启试试)。
  • 查看日志,确认与Seata是否连接通信,是否正常注册TM、RM等。

由于觉得是小问题,所以从下午4点左右到6点,一直在做上述的4个事情。

结果就是,没发现步骤问题,也没发现配置问题,与Seata Server也正常通信,但是“分支事务不回滚”的问题还在。这个时候我其实有点意识到这可能不是个小问题了,有点小慌。但是搞了一段时间没头绪,脑子也乱了索性就下班回家了。

“坏了!见鬼了!”

本来想着第二天上班再处理的,但是被这个问题搞得实在睡不着,晚上11点半打开电脑继续处理。处理的过程和下午一样,我还是觉得可能是哪里不小心漏掉了步骤或者配置不对,扩大了检查范围,除了检查代码中的配置、数据库,还检查了Nacos Server的配置、Seata Server的配置,结果发现配置没问题,也没有遗漏什么。

为什么我觉得一开始是个小问题,然后主要是在检查配置项之类的。其原因就是之前在上一版已经整合过、配置过,而且验证通过,源码也没问题,分布式事务的处理结果是正确的。因此,我肯定会觉得只要整合步骤没有遗漏、配置项正确,分布式事务肯定会被正常处理。同样的代码、同样的配置、同样的测试环境,一个正常,一个不正常,这有点出乎意料了。这个时候我意识到了问题的严重性了,我觉得可能是遇见鬼了。确切地说,当时有点麻了,一份代码中分布式事务正常处理,一份完全没反应,说不麻是假的。

凌晨了,换个思路吧。

冷静下来后,我也不骗自己了,这代码肯定是有问题的,不然分支事务怎么会不回滚。但是我确实不知道问题在哪,怎么同样的东西在这一版项目里就不能用了呢?

不过,再去查配置、对比代码已经没意义了。既然确认代码有问题(不嘴硬了),那就开始根据Seata运行流程查一下哪里出了问题吧,主要是根据微服务实例的运行日志和Seata Server的运行日志来查的。

  • 与Seata Server通信正常。
  • 三个微服务实例都正常注册TM、RM等。
  • 全局事务正常开启。
  • 两个分支事务开启的日志一行都没出现。

不管是微服务实例的运行日志和Seata Server的运行日志,都没有看到两个分支事务的开启和处理,是的,没有任何信息和踪迹。再去数据库中确认了一下,undo_log表中也并没有数据。虽然不知道哪里出了问题,但是至少有方向了。

全局事务能够正常开启和回滚,而两个分支事务不正常(与Seata Server正常通信,但是都没有生效)。到这里已经大致有了眉目,乘客微服务和订单微服务两个服务实例的运行日志和Seata Server的运行日志,都没有看到任何关于全局事务的信息,这也说明了两个分支事务可能根本就没有注册成功。全局事务正常开启和处理,而两个本应出现的分支事务没有出现,它们之间“失联”了。

从代码层面来说,全局事务和分支事务的联系主要在一个变量上,这个变量就是全局事务的ID——xid。现在它们“失联”了,只能通过这个变量的产生、传递、接收、处理等几个步骤来确认问题在哪里了。

此时的要检查的内容就确定了下来:

  • 全局事务是否正常开启?xid是否正确地生成了?
  • xid是否正确地传递给下游的调用实例中?
  • 下游的调用实例是否正确地接到了xid?
  • 接到xid后是否正确处理并且开启分支事务?

“问题不清晰,看源码分析”。

为了确认上述的几个检查内容,还是要用debug模式看一看Seata处理分布式事务过程中所涉及到的源码,由于牵涉的源码太多,这里笔者挑几个重要节点介绍一下。

对于“全局事务是否正常开启?xid是否正确地生成了?”,主要跟进了下方两个类的源码:

io.seata.spring.annotation.GlobalTransactionalInterceptor.java

io.seata.tm.api.TransactionalTemplate.java

这两个类主要涉及全局事务的开启和处理,感兴趣的读者可以仔细地去探索一下。当然,结果是这个步骤并没有问题,全局事务正常开启,xid正确生成。

难道是xid生成了却没有传递给下游?对于这个问题,笔者主要在debug模式下跟进了com.alibaba.cloud.seata.feign.SeataFeignClient.java这个类的源码:

@Override
public Response execute(Request request, Request.Options options) throws IOException {
   

    Request modifiedRequest = getModifyRequest(req
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值