Seata源码(二)事务基础对象


Java极客 | 作者 / 铿然一叶 这是Java极客的第 93 篇原创文章


相关阅读:

萌新快速成长之路
JAVA编程思想(一)通过依赖注入增加扩展性
JAVA编程思想(二)如何面向接口编程
JAVA编程思想(三)去掉别扭的if,自注册策略模式优雅满足开闭原则
JAVA编程思想(四)Builder模式经典范式以及和工厂模式如何选?
Java编程思想(五)事件通知模式解耦过程
Java编程思想(六)事件通知模式解耦过程
Java编程思想(七)使用组合和继承的场景
JAVA基础(一)简单、透彻理解内部类和静态内部类
JAVA基础(二)内存优化-使用Java引用做缓存
JAVA基础(三)ClassLoader实现热加载
JAVA基础(四)枚举(enum)和常量定义,工厂类使用对比
JAVA基础(五)函数式接口-复用,解耦之利刃
HikariPool源码(二)设计思想借鉴
【极客源码】JetCache源码(一)开篇
【极客源码】JetCache源码(二)顶层视图
人在职场(一)IT大厂生存法则


1. 全局事务注解

属性 描述
timeoutMills 全局事务超时时间,默认值60秒,配置参数:client.tm.default-global-transaction-timeout将重载默认值
name 全局事务名称,若不设置,取注解的实际方法名
rollbackFor 要做回滚的异常类
rollbackForClassName 要做回滚的异常类名
noRollbackFor 不做异常回滚的异常类
noRollbackForClassName 不做回滚处理的异常类名
propagation 事务传播级别,默认值:REQUIRED
lockRetryInternal 获取全局锁重试间隔时间(ms),重载配置参数:client.rm.lock.retryInterval
lockRetryTimes 获取全局锁的重试次数,重载配置参数:client.rm.lock.retryTimes

2. GlobalLock注解

属性 描述
lockRetryInternal 获取全局锁重试间隔(ms),对应配置参数:client.rm.lock.retryInterval
lockRetryTimes 获取全局锁重试次数,对一个配置参数:client.rm.lock.retryTimes

3. 事务传播级别

Propagation.java

传播级别 描述
REQUIRED 默认级别,如果事务存在,则执行当前事务,否则开启新事务
REQUIRES_NEW 如果事务存在,则挂起当前事务,开启一个新任务,类似事务嵌套
NOT_SUPPORTED 如果事务存在,则挂起当前事务,待执行的业务方法不开启事务
SUPPORTS 如果事务不存在,执行业务方法不开启事务,否则执行业务方法使用当前事务
NEVER 如果事务存在,则抛出异常,否则待执行业务方法不开启事务
MANDATORY 如果事务不存在,则抛出异常,否则使用当前事务执行业务方法

4. 事务状态

GlobalStatus.java 事务状态如下:

其他状态都好理解,主要描述下面几种状态:

状态 描述
Unknown 默认的状态,此时事务还没开启,没有生成XID
Begin 事务开始
Finished GlobalSession为null时将置为此状态。Saga一阶段提交失败也会置为此状态。

5. 事务角色

GlobalTransactionRole.java

角色 描述
Launcher 全局事务开始者,根据此角色判断是否开启全局事务
Participant 全局事务参与者,根据此角色判断是否做commit和rollback

5.1 Launcher角色

5.1.1 初始化

TransactionalTemplate.execute方法中,事务传播级别为REQUIRES_NEW时,创建新事务:

java 复制代码
                case REQUIRES_NEW:
                    // If transaction is existing, suspend it, and then begin new transaction.
                    if (existingTransaction(tx)) {
                        suspendedResourcesHolder = tx.suspend();
                        tx = GlobalTransactionContext.createNew();
                    }

TransactionalTemplate.execute方法中,如果事务不存在,则创建新事务,简而言之是在入口方法上调用时创建新事务:

java 复制代码
            if (tx == null) {
                tx = GlobalTransactionContext.createNew();
            }

具体的创建事务方法: GlobalTransactionContext.java

java 复制代码
    public static GlobalTransaction createNew() {
        return new DefaultGlobalTransaction();
    }

接着设置角色为Launcher: DefaultGlobalTransaction.java

java 复制代码
    DefaultGlobalTransaction() {
        this(null, GlobalStatus.UnKnown, GlobalTransactionRole.Launcher);
    }

5.1.2 使用

在开启事务中使用到Launcher角色: TransactionalTemplate.java

java 复制代码
    private void beginTransaction(TransactionInfo txInfo, GlobalTransaction tx) throws TransactionalExecutor.ExecutionException {
        try {
            triggerBeforeBegin();
            // 在开启事务中使用到Launcher角色
            tx.begin(txInfo.getTimeOut(), txInfo.getName());
            triggerAfterBegin();
        } catch (TransactionException txe) {
            throw new TransactionalExecutor.ExecutionException(tx, txe,
                TransactionalExecutor.Code.BeginFailure);

        }
    }

如果不是Launcher角色,则直接返回,不开启事务,否则获取XID,开启事务: DefaultGlobalTransaction.java

java 复制代码
   public void begin(int timeout, String name) throws TransactionException {
        // 如果不是Launcher角色,则直接返回,不开启事务
        if (role != GlobalTransactionRole.Launcher) {
            assertXIDNotNull();
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Ignore Begin(): just involved in global transaction [{}]", xid);
            }
            return;
        }
        assertXIDNull();
        String currentXid = RootContext.getXID();
        if (currentXid != null) {
            throw new IllegalStateException("Global transaction already exists," +
                " can't begin a new global transaction, currentXid = " + currentXid);
        }
        xid = transactionManager.begin(null, null, name, timeout);
        status = GlobalStatus.Begin;
        RootContext.bind(xid);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Begin new global transaction [{}]", xid);
        }
    }

5.2 Participant角色

5.2.1 初始化

获取当前事务时判断XID是否存在,存在则返回null,否则创建新事物,角色为Participant: GlobalTransactionContext.java

java 复制代码
    public static GlobalTransaction getCurrent() {
        String xid = RootContext.getXID();
        if (xid == null) {
            return null;
        }
        return new DefaultGlobalTransaction(xid, GlobalStatus.Begin, GlobalTransactionRole.Participant);
    }

获取当前事务调用点: TransactionalTemplate.java

java 复制代码
    public Object execute(TransactionalExecutor business) throws Throwable {
        // 1. Get transactionInfo
        TransactionInfo txInfo = business.getTransactionInfo();
        if (txInfo == null) {
            throw new ShouldNeverHappenException("transactionInfo does not exist");
        }
        // 1.1 Get current transaction, if not null, the tx role is 'GlobalTransactionRole.Participant'.
        GlobalTransaction tx = GlobalTransactionContext.getCurrent();

5.2.2 使用

在事务提交和回滚时,如果是Participant角色则不做处理,

事务提交: DefaultGlobalTransaction.java

java 复制代码
    public void commit() throws TransactionException {
        // commit,非Launcher角色则直接返回
        if (role == GlobalTransactionRole.Participant) {
            // Participant has no responsibility of committing
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Ignore Commit(): just involved in global transaction [{}]", xid);
            }
            return;
        }
        assertXIDNotNull();
        int retry = COMMIT_RETRY_COUNT <= 0 ? DEFAULT_TM_COMMIT_RETRY_COUNT : COMMIT_RETRY_COUNT;
        try {
            while (retry > 0) {
                try {
                    status = transactionManager.commit(xid);
                    break;
                } catch (Throwable ex) {
                    LOGGER.error("Failed to report global commit [{}],Retry Countdown: {}, reason: {}", this.getXid(), retry, ex.getMessage());
                    retry--;
                    if (retry == 0) {
                        throw new TransactionException("Failed to report global commit", ex);
                    }
                }
            }
        } finally {
            if (xid.equals(RootContext.getXID())) {
                suspend();
            }
        }
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("[{}] commit status: {}", xid, status);
        }
    }

commit被调用点: TransactionalTemplate.java

java 复制代码
    private void commitTransaction(GlobalTransaction tx) throws TransactionalExecutor.ExecutionException {
        try {
            triggerBeforeCommit();
            tx.commit();
            triggerAfterCommit();
        } catch (TransactionException txe) {
            // 4.1 Failed to commit
            throw new TransactionalExecutor.ExecutionException(tx, txe,
                TransactionalExecutor.Code.CommitFailure);
        }
    }

事务回滚:

java 复制代码
    public void rollback() throws TransactionException {
        if (role == GlobalTransactionRole.Participant) {
            // Participant has no responsibility of rollback
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Ignore Rollback(): just involved in global transaction [{}]", xid);
            }
            return;
        }
        assertXIDNotNull();

        int retry = ROLLBACK_RETRY_COUNT <= 0 ? DEFAULT_TM_ROLLBACK_RETRY_COUNT : ROLLBACK_RETRY_COUNT;
        try {
            while (retry > 0) {
                try {
                    status = transactionManager.rollback(xid);
                    break;
                } catch (Throwable ex) {
                    LOGGER.error("Failed to report global rollback [{}],Retry Countdown: {}, reason: {}", this.getXid(), retry, ex.getMessage());
                    retry--;
                    if (retry == 0) {
                        throw new TransactionException("Failed to report global rollback", ex);
                    }
                }
            }
        } finally {
            if (xid.equals(RootContext.getXID())) {
                suspend();
            }
        }
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("[{}] rollback status: {}", xid, status);
        }
    }

rollback被调用点: TransactionalTemplate.java

java 复制代码
    private void rollbackTransaction(GlobalTransaction tx, Throwable originalException) throws TransactionException, TransactionalExecutor.ExecutionException {
        triggerBeforeRollback();
        tx.rollback();
        triggerAfterRollback();
        // 3.1 Successfully rolled back
        throw new TransactionalExecutor.ExecutionException(tx, GlobalStatus.RollbackRetrying.equals(tx.getLocalStatus())
            ? TransactionalExecutor.Code.RollbackRetrying : TransactionalExecutor.Code.RollbackDone, originalException);
    }

end.


<--阅过留痕,左边点赞!


相关推荐
Java编程乐园几秒前
Java中以某字符串开头且忽略大小写字母如何实现【正则表达式(Regex)】
java·正则表达式
阿七想学习1 分钟前
数据结构《排序》
java·数据结构·学习·算法·排序算法
xlsw_7 分钟前
java全栈day21--Web后端实战之利用Mybaits查询数据
java·开发语言
什么想法都无21 分钟前
stream
java·java stream
m0_7482336422 分钟前
WebService简介
java
love静思冥想22 分钟前
Stream `Collectors.toList()` 和 `Stream.toList()` 的区别(Java)
java·stream
Ch.yang41 分钟前
【Spring】 Bean 注入 HttpServletRequest 能保证线程安全的原理
java·spring·代理模式
web1508509664143 分钟前
基于Mysql、JavaScript、PHP、ajax开发的MBTI性格测试网站(前端+后端)
java
昙鱼1 小时前
springboot创建web项目
java·前端·spring boot·后端·spring·maven
eternal__day1 小时前
数据结构(哈希表(中)纯概念版)
java·数据结构·算法·哈希算法·推荐算法