Seata客户端事务模型源码解析


前言

  io.seata.tm.api.TransactionalTemplateexecute方法,是Seata客户端事务模型的体现:
在这里插入图片描述
  本篇主要介绍客户端开启事务,执行事务,提交/回滚与服务端的交互。


一、beginTransaction

  beginTransaction是开启事务的方法,其中tx.begin(txInfo.getTimeOut(), txInfo.getName());是真正和服务端通信,开启事务的方法,而triggerBeforeBegin();triggerAfterBegin();则是TransactionHookManager的两个钩子方法,作为开启事务前后的一些处理:
在这里插入图片描述

1.1、扩展点:TransactionHook

  事务钩子存放在TransactionHookManagerLOCAL_HOOKS中,和当前线程是绑定的。
在这里插入图片描述
  如果需要自定义,需要实现TransactionHook接口,重写其中的方法:

public class MyTransactionHook implements TransactionHook {

    @Override
    public void beforeBegin() {
        System.out.println("Global Transaction is about to begin...");
    }

    @Override
    public void afterBegin() {
        System.out.println("Transaction started.");
    }

    @Override
    public void beforeCommit() {
        System.out.println("Transaction commit about to happen...");
    }

    @Override
    public void afterCommit() {
        System.out.println("Transaction committed.");
    }

    @Override
    public void beforeRollback() {
        System.out.println("Transaction rollback about to happen...");
    }

    @Override
    public void afterRollback() {
        System.out.println("Transaction rolled back.");
    }
}

  并且还要将其注册到TransactionHookManager中:(调用io.seata.tm.api.transaction.TransactionHookManager#registerHook),并且@GlobalTransactional方法执行时,Hook必须已在同一个线程中注册好。可以通过Spring aop扩展点实现:

@Aspect
@Component
public class SeataHookRegisterAspect {

    @Autowired
    private MyTransactionHook myTransactionHook;

    @Pointcut("@annotation(io.seata.spring.annotation.GlobalTransactional)")
    public void pt() {}

    @Around("pt()")
    public Object registerHookAndProceed(ProceedingJoinPoint pjp) throws Throwable {
        // 注册 Hook(在当前线程)
        TransactionHookManager.registerHook(myTransactionHook);

        try {
            return pjp.proceed();
        } finally {
            // 清理 Hook,防止线程复用污染
            TransactionHookManager.clear();
        }
    }
}

1.2、begin

  在begin方法中,大致可以分为三个步骤:

  1. 从上下文中获取XID。如果能获取到,就抛出异常(有XID表示当前线程已经存在事务)。
  2. 与服务端交互,开启事务,获得服务端返回的XID
  3. 将XID绑定到当前线程中。

在这里插入图片描述


  为什么从上下文中获取XID。如果能获取到,就直接抛出异常不需要考虑事务传播级别吗
  关于这个问题,首先来看一下,下图是Spring事务的传播行为:
在这里插入图片描述
  下图是Seata事务的传播行为:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  仔细观察,有没有发现Seata的少了一个NESTED嵌套事务?NESTED嵌套事务是基于数据库的save point实现的** Seata 本身是基于“全局唯一事务上下文(XID)”来控制整个分布式事务流程的,它并不像数据库那样支持隔离级别配置或嵌套事务,Seata 的设计是每个线程中同时只能有一个全局事务。**一旦发现当前线程的 RootContext 中已经有 XID,意味着这个线程已经处于某个全局事务中,不允许再嵌套开启另一个全局事务。


  在begin方法中,会去请求Seata服务端,开启事务并拿到XID。如果服务端返回的是失败,则直接抛出异常。
在这里插入图片描述

二、commitTransaction

  事务的提交,分为两种情况,第一种是正常提交,第二种是业务执行的过程中出现了异常。但是异常不在处理范围内,体现在completeTransactionAfterThrowing方法中:
在这里插入图片描述
  在提交事务之前,首先还会判断是否超时(默认60000ms),如果超时了一样会回滚。
在这里插入图片描述
  在真正执行事务提交之前和之后,还会有两个钩子方法,作为扩展点。
在这里插入图片描述

2.1、tx.commit()

  事务提交之前,首先会去判断当前的角色,是否是事务的发起者TM,如果是事务的参与者RM,则不能进行提交。
在这里插入图片描述
  事务的提交加入了重试机制。
在这里插入图片描述


  commit方法同样是去向Seata服务端发起请求,最终得到一个状态。

在这里插入图片描述


  最后还会调用suspend方法清理当前线程的事务上下文

在这里插入图片描述


在这里插入图片描述


  事务提交后,还会进行一系列的判断。
在这里插入图片描述
  首先拿到afterCommitStatus,这个afterCommitStatus实际上就是服务端返回的状态,是DefaultGlobalTransactionstatus属性:
在这里插入图片描述
  然后将全局事务的最终状态映射为TransactionalExecutor.Code类型,供业务层判断事务的最终执行结果:

  • TimeoutRollbacking → Rollbacking:当前全局事务已经超时,并正在被TC触发自动回滚中。此时commit()发起失败,TC 决定自动回滚。
  • TimeoutRollbacked → RollbackDone:当前事务已经因为超时而自动回滚成功,业务提交之前事务已经终止。
  • Finished → CommitFailure:意味着事务已经终止,但不是正常提交成功的状态,可能是本地以为提交成功,但 TC 可能并未完成事务提交,或提交失败。
  • default:默认为提交成功。

在这里插入图片描述
  最终会对异常状态进行处理:

  • 两阶段提交发生了不一致,事务已经终止,但不是正常提交成功的状态。
  • 业务还未提交前,TC 已经因为超时将其回滚。

在这里插入图片描述

2.2、rollbackTransaction

  当执行目标逻辑发生了异常,并且异常类型可以被处理,就会执行rollbackTransaction回滚的逻辑。
在这里插入图片描述
  在rollbackTransaction中,首先还是在真正执行回滚的前后,插入钩子方法。然后根据服务端返回的状态,去映射为TransactionalExecutor.Code类型,最终会抛出异常。
在这里插入图片描述
  tx.rollback();的关键点,和tx.commit()是相同的。

在这里插入图片描述


总结

  Seata 客户端(微服务端)在事务处理上遵循与传统事务编程模型一致的理念,这与 Spring 的编程风格保持高度契合。不同之处在于:事务的开启、提交以及回滚并非本地直接完成,而是通过与 Seata 事务协调器(TC)进行通信,由服务端统一调度管理。
  在整个事务生命周期中,XID(全局事务ID) 是唯一标识符,作为贯穿始终的上下文信息,它驱动各个参与的微服务执行具体的分支事务逻辑。微服务在完成本地事务操作后,需将处理结果(如分支提交或回滚)上报至 TC,由 TC 汇总所有分支状态并决定最终的全局事务走向。
  此外,Seata 在事务处理流程中提供了相应的钩子方法(如 beforeBegin、afterCommit、afterRollback 等),允许开发者通过子类或扩展方式注入自定义逻辑,增强事务控制的灵活性与可扩展性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值