记一次错误集成Seata分布式事务组件的经历

先说结论,这是一个非常愚蠢的问题,一个错误的开始,怎么也不可能有正确的结果。

最近在研究SpringCloud,于是学习了一下Ruoyi Cloud版本,本来一切都很顺利,直到在集成Seata分布式任务的时候,使用 @GlobalTransactional 连用 @Transactional的时候,出现了问题,始终会报一个错误:

Could not register branch into global session xid = xxx status = Rollbacking while expecting Begin

org.springframework.transaction.TransactionSystemException: JDBC commit failed; nested exception is java.sql.SQLException: io.seata.core.exception.RmTransactionException: 
Response[ TransactionException[Could not register branch into global session xid = xxx status = Rollbacking while expecting Begin] ]

但是去掉本地事务注解就好了,我原先以为是全局事务不能和本地事务同时存在,也以为是版本问题导致手动回滚错误,其实在官网上有相应的异常解释,直到我反复的阅读官网文档,和翻遍了issus,仍然确定我没有这几个问题,并且官网很确定这两个注解是可以同时使用的

file

file

最后我确定了,是我的错误理解导致了这个错误,原因就是我没有在TM端抛出异常,导致事务继续走了commit路径。

我的测试场景是有2个服务,A和B,在入口 A 服务的入口service中,加了@GlobalTransactional+@Transactional 注解,方法内容是

public void fun(){
    A服务本地insert1();
    call B服务(); //主动报错 int i = 1/0;
    A服务本地insert2();
}

在B服务中,使用的是AOP切面的方法,使用了手动回滚,这也是 Seata 自身提供的API 用于全局事务回滚 GlobalTransactionContext.reload(RootContext.getXID()).rollback(); 接着正常走下面的流程,在A服务中也并未对B服务产生的异常做任何处理

也就是我错误的认为TM端,也就是加了 @GlobalTransactional 注解那边,Seata 会接管我的本地事务,在TC端收到B服务上报的全局回滚后,会将其他的RM程序自动操作停止和回滚,其实并不是,虽然我反复看了很多遍官方文档,以及两段式提交协议相关内容(惭愧惭愧),但是我漏了一个关键字,直到我后来在官方博客中看到,那就是 非侵入式。

文中是这么写道的:

Seata AT 模式是一种非侵入式的分布式事务解决方案,Seata 在内部做了对数据库操作的代理层,我们使用 Seata AT 模式时,实际上用的是 Seata 自带的数据源代理 DataSourceProxy,Seata 在这层代理中加入了很多逻辑,比如插入回滚 undo_log 日志,检查全局锁等。

所以一看到这里就明白了 Seata 并不接管本身的事务,只是在事务做了一层代理,在TM开启全局事务后,每一个注册过的 RM 的本地事务对数据库的读写操作,都将反操作在同一个事务中写到对应的RM本身的undo表中去,以便在全局回滚的时候操作。换句话说,全局事务只对分支事务的undo负责,如果有本地事务未提交,本地的undo表啥也没有,并不会出问题,

由于我没有在B服务手动回滚后,在A服务里抛出异常或处理,导致TC那边确实认为这次事务要回滚,整体也确实没问题,但是最后A服务没有感知到异常,仍然会走commit操作,回去重新向TC注册,但是在TC确认的时候会告诉他,对应的XID当前状态是Rollbacking,并不是Begin,不能注册,也就出现了开头的错误。

站内相关文章:

Comment ()
如果您有不同的看法,或者疑问,欢迎指教