【SEATA源码分析】 tm 模块源码解析

一 .导读

首先放一张tm模块的主要类关系图:
在这里插入图片描述
从图中可以很明显的看出,模块暴露出去的类是 TransactionalTemplate ,模板类中找 GlobalTransactionalContext 类获取业务业务类,而业务类持有了一个 TrancationManagerHolder 类,即由 TransactionManager 的实现类封装成的单例持有类,holder 通过SPI的方式加载配置文件中配置的具体的 TransactionManager 的实现类,0.9的版本默认实现类是 DefaultTransactionManager,这个类即模板方法处理业务的类。

上面这五个类,真正有用的就是两个类,一个template和一个defaultTransactionManager,一个模板方法类,一个具体的业务实现类,而tm封装这么多,是为了增加扩展点,为将来的扩展留下空间。

比如你可以修改SPI配置文件中 TransactionManager 的实现类,来扩展模板方法的实现。甚至你还可以替换 GlobalTrancationalInterceptor(Spring模块) 中持有的 TrancactionalTemplate,彻底告别tm模块,自己实现具体的业务。

二 . model类分析

首先是 template 模板参数需要的类 TransactionalExecutor ,这是一个接口类,
在这里插入图片描述
execute()是用来执行业务逻辑的类,而 getTransactionInfo() 用来获取 @GlobalTransactional 注解的信息。
GlobalTransactionalInterceptor 在调用模板类的 execute(transactionalExecutor)方法的时候,注入了业务执行逻辑的类和从注解上获取到的信息,即实现了上面的接口类,是匿名类的形式。

TransactionalExecutor 接口类中还有一个子类,即 Exception 的子类 ExecutionException ,是对执行时异常的封装。

还有一个enum类,对失败步骤记录状态的集合的封装。

接下来是 TransactionInfo 类,类中的属性用来记录 @GlobalTransactional 的信息,注解中的 rollbackFor 和 noRollbackFor 的异常类,根据一场的类名,封装成了 RoolbackRule 和 NoRollbackRule ,NoRollbackRule 是 RollbackRule 的子类,info类中有一个判断参数中异常类是否要回滚的遍历的方法很有意思:
在这里插入图片描述
这个方法中有一个很重要的 deepest ,刚开始我还以为这个变量没什么用,因为只要返回 boolean ,不需要知道到底是 roolbackRules 中的哪一个起作用了,还在github上提了一个 pr ,结果被大佬教育了。。。

其实这里 deepest 是用来确定,winner 的是子类,父类的 depth 会高一点。而 roolbackRules中放置的是回滚和不回滚的类的集合。

roolbackRules这个集合是 LinkHashSet ,即如果有相同的异常(因为RollbackRule重写了 hashcode(exceptionName),所以这里的相同是指 exceptioName 相同),后面的类会覆盖前面的,在 template 类中,解析注解 @GlobalTransactional 顺序是先解析回滚的,再解析不回滚的,所以这里如果回滚的和不回滚的有相同的异常,最终效果是不回滚。

三 .模板类分析

模板类的 execute(TransactionalExecutor) 方法很清晰,下面一步一步的来分析:
在这里插入图片描述
1.获取当前的全局事务(能获取到xid)或者创建一个全局事务(xid暂时为空),全局事务有一个属性 enum 类型,有两个角色,一个是 Launcher ,一个是 Participant ,即发起者或者参与者。

2.开始全局事务:这里最终会调用 DefaultTransactionManager 即 TM 去向 TC 注册全局事务,获取注册的状态,如果成功会有 xid,并绑定到上下文中。参与者全局事务在注册之前就可以获取到xid。

3.执行业务就不说了,直接看如果执行业务出现异常:这里需要根据 TransactionInfo(@GlobalTransaction) 的 roolbackOn(ex) 判断是否是需要回滚的异常,如果不是,执行4提交事务。如果是,则调用DTM进行异常的回滚,并且获取本次回滚后的状态,绑定到属性中,并抛出异常。

4.上报事务的提交结果。角色是参与者的事务是没有职责上报状态的。

5.移除所有的Hook类,即钩子类。

上述模板流程,如果是成功的,则返回执行结果,如果是失败的,则抛出导致失败的异常,交给上层处理。

上层调用类 GlobalTransactionInterceptor 也没有对业务产生的异常进行处理,直接抛给了上层。这里的上层即是 srping 管理的范围,spring 会对异常进行处理。

在 template 方法中,如果因为 seata 框架对事务流程的处理而抛出的异常,则由模板类封装对应的异常,上述5个步骤,每一个步骤都有对应的异常,模板方法把异常抛给 GlobalTransactionInterceptor ,在这个类中,先是调用 DefaultFailureHandlerImpl 处理,然后再抛给上层,即 spring 处理。

四 .流程的异常处理

现在的版本0.9,有一个 failureHandler 的默认处理类 DefaultFailureHandler ,这个类维持了一个 HashedWheelTimer(netty包中) 周期性执行任务的线程池类,目前池中只实例化了一个线程。这个类和 newSchedulerThreadPool 生成的线程池类似,都是周期性的执行任务,seata没有自己写周期性执行任务的线程池,而是复用了netty的。

这个线程池的任务是输出当前事务的状态,即 DefaultGlobalTransaction 中 status 的状态:
在这里插入图片描述
这里对事务状态的判断比较难理解,这里用提交失败处理来分析:
当发生 onCommitFailure(),给线程池增加一个任务,

timer.newTimeout(new CheckTimerTask(tx, GlobalStatus.Committed), SCHEDULE_INTERVAL_SECONDS, TimeUnit.SECONDS);

shouldShop()中会判断,interceptor 传递过来的 tx 中 status 的状态,是否是 GlobalStatus.commited ,即是否是提交完成的状态。

这里肯定不会是提交完成的状态,因为提交完成不会抛出异常,就不会进入 failureHandler 的处理方法中。所以 tx 中的 status 的状态和 required 不一样,返回false,这个 run() 方法结束。

这里另起一个线程,主要是为了进行状态的校验,确定一下失败操作是否是正确的,并且输出日志:

LOGGER.info("transaction[" + tx.getXid() + "] current status is [" + status + "]");

结合上面 onCommitFailureHandler() 中的日志:

LOGGER.warn("Failed to commit transaction[" + tx.getXid() + "]", cause);

一个警告级别,输出未能提交事务的异常原因,一个info级别,输出当前事务的状态。便于定位处理异常。

五 .总结

至此 tm 模块就分析完了,这个模块代理被 @GlobalTransaction 注解的方法,纳入自己的事务处理中。
对外表现是:
1.提供了 template 模板处理流程的方法,供 spring 模块的 interceptor 调用。
2.代理了业务类,等于包裹了业务类,业务类的前置后置都有处理逻辑。
3.发生业务异常时,事务流程处理完毕,还会把异常抛给上层,即 spring 处理。

更多好文请关注微信公众号:我手一杯
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值