【深入理解SpringCloud微服务】Seata(AT模式)源码解析——全局事务的回滚

Seata(AT模式)源码解析------全局事务的回滚

  • [一、TM发起全局事务回滚:TransactionalTemplate#completeTransactionAfterThrowing(TransactionInfo, GlobalTransaction, Throwable)](#completeTransactionAfterThrowing(TransactionInfo, GlobalTransaction, Throwable))
  • [二、TC接收全局事务回滚请求:DefaultCoordinator#doGlobalRollback(GlobalRollbackRequest, GlobalRollbackResponse, RpcContext)](#doGlobalRollback(GlobalRollbackRequest, GlobalRollbackResponse, RpcContext))
  • [三、TC处理全局事务回滚:DefaultCore#doGlobalRollback(GlobalSession globalSession, boolean retrying)](#doGlobalRollback(GlobalSession globalSession, boolean retrying))
  • 四、RM处理分支事务回滚:AbstractRMHandler#handle(BranchRollbackRequest)

如果全局事务的调用链上某个节点发生异常了,会被TM捕获到,然后发起全局事务回滚。

TransactionalTemplate#execute(TransactionalExecutor)

java 复制代码
            try {
                // 开启全局事务
                beginTransaction(txInfo, tx);

                Object rs;
                try {
                    // 执行业务逻辑
                    rs = business.execute();
                } catch (Throwable ex) {
                    // 回滚全局事务(本篇文章分析的重点)
                    completeTransactionAfterThrowing(txInfo, tx, ex);
                    throw ex;
                }

                // 提交全局事务
                commitTransaction(tx);

                return rs;
            } finally {}

一、TM发起全局事务回滚:TransactionalTemplate#completeTransactionAfterThrowing(TransactionInfo, GlobalTransaction, Throwable)

java 复制代码
    private void completeTransactionAfterThrowing(TransactionInfo txInfo, GlobalTransaction tx, Throwable originalException) throws TransactionalExecutor.ExecutionException {
        if (txInfo != null && txInfo.rollbackOn(originalException)) {
            try {
            	// 如果异常类型与@GlobalTransaction的rollbackFor属性声明的匹配,才会回滚
                rollbackTransaction(tx, originalException);
            } catch () {}
        } else {
            // 异常类型与@GlobalTransaction的rollbackFor属性声明的不匹配,
            // 因此不会滚,还是提交
            commitTransaction(tx);
        }
    }

txInfo.rollbackOn(originalException)的作用是判断当前的异常类型是否与@GlobalTransaction注解的rollbackFor属性声明的匹配,这意味着当前异常必须是@GlobalTransaction注解的rollbackFor属性声明的异常类型或子类。

如果异常类型匹配,才会进行全局事务回滚,否则还是进行全局事务提交。

TransactionalTemplate#rollbackTransaction(GlobalTransaction, Throwable)

java 复制代码
    private void rollbackTransaction(GlobalTransaction tx, Throwable originalException) throws TransactionException, TransactionalExecutor.ExecutionException {
    	// 发起全局事务回滚
        tx.rollback();
        // 回滚成功后,还要把异常抛出去
        throw new ...;
    }

DefaultGlobalTransaction#rollback()

java 复制代码
    @Override
    public void rollback() throws TransactionException {
    	// 重试次数
        int retry = ROLLBACK_RETRY_COUNT <= 0 ? DEFAULT_TM_ROLLBACK_RETRY_COUNT : ROLLBACK_RETRY_COUNT;
        try {
        	// 在while循环钟进行全局事务回滚,不成功可以重试
            while (retry > 0) {
                try {
                	// 发起全局事务回滚
                    status = transactionManager.rollback(xid);
                    break;
                } catch (Throwable ex) {
                    retry--;
                    if (retry == 0) {
                        throw new TransactionException("Failed to report global rollback", ex);
                    }
                }
            }
        } finally {}
    }

DefaultTransactionManager#rollback(String xid)

java 复制代码
    @Override
    public GlobalStatus rollback(String xid) throws TransactionException {
    	// 创建请求对象
        GlobalRollbackRequest globalRollback = new GlobalRollbackRequest();
        // 设置全局事务id到请求对象中
        globalRollback.setXid(xid);
        // 调用syncCall方法,通过Netty向TC发起全局事务回滚的请求
        GlobalRollbackResponse response = (GlobalRollbackResponse) syncCall(globalRollback);
        return response.getGlobalStatus();
    }

可以看到最后就是通过Netty向TC发起全局事务回滚的请求,请求对象GlobalRollbackRequest携带了xid。

二、TC接收全局事务回滚请求:DefaultCoordinator#doGlobalRollback(GlobalRollbackRequest, GlobalRollbackResponse, RpcContext)

当TC接收到请求后,会调用DefaultCoordinator的doGlobalRollback方法处理请求。

java 复制代码
    @Override
    protected void doGlobalRollback(GlobalRollbackRequest request, GlobalRollbackResponse response,
                                    RpcContext rpcContext) throws TransactionException {
		// 调用DefaultCore的rollback                                    方法处理请求
        response.setGlobalStatus(core.rollback(request.getXid()));
    }

DefaultCoordinator的doGlobalRollback方法调用DefaultCore的rollback方法。

DefaultCore#rollback(String xid)

java 复制代码
    @Override
    public GlobalStatus rollback(String xid) throws TransactionException {
    	// 根据xid查询global_table表,返回GlobalSession对象
        GlobalSession globalSession = SessionHolder.findGlobalSession(xid);

        boolean shouldRollBack = SessionHolder.lockAndExecute(globalSession, () -> {
            globalSession.close();
            if (globalSession.getStatus() == GlobalStatus.Begin) {
            	// 修改global_table表对应记录的状态为Rollbacking
                globalSession.changeStatus(GlobalStatus.Rollbacking);
                return true;
            }
            return false;
        });
		// 全局事务回滚处理
        doGlobalRollback(globalSession, false);
        return globalSession.getStatus();
    }

先根据xid查询globa_table表获取到对应的GlobalSession对象,然后修改globa_table对应记录的状态为Rollbacking,最后调用doGlobalRollback进行全局事务回滚处理。

三、TC处理全局事务回滚:DefaultCore#doGlobalRollback(GlobalSession globalSession, boolean retrying)

java 复制代码
    @Override
    public boolean doGlobalRollback(GlobalSession globalSession, boolean retrying) throws TransactionException {
        boolean success = true;

        if (globalSession.isSaga()) {
        	// saga模式的处理逻辑,忽略
        } else {
        	// 获取与当前全局事务关联的分支事务信息
            Boolean result = SessionHelper.forEach(globalSession.getReverseSortedBranches(), branchSession -> {
                BranchStatus currentBranchStatus = branchSession.getStatus();
                if (currentBranchStatus == BranchStatus.PhaseOne_Failed) {
                	// 删除一阶段失败的分支事务
                    globalSession.removeBranch(branchSession);
                    return CONTINUE;
                }
                try {
                	// 发起rpc远程调用通知RM回滚分支事务
                    BranchStatus branchStatus = branchRollback(globalSession, branchSession);
                    switch (branchStatus) {
                        case PhaseTwo_Rollbacked:
                        	// 分支事务回滚成功,删除branch_table表记录
                            globalSession.removeBranch(branchSession);
                            return CONTINUE;
                        case PhaseTwo_RollbackFailed_Unretryable:
                            return false;
                        default:
                            return false;
                    }
                } catch (Exception ex) {}
            });

        if (success) {
        	// 修改global_table表对应记录状态为Rollbacked,然后释放全局锁
            SessionHelper.endRollbacked(globalSession);
        }
        return success;
    }

查询与当前全局事务关联的分支事务,然后遍历每一个分支事务,请求对应的RM回滚分支事务,如果回滚成功,就删除branch_table表对应的分支事务记录。

修改global_table表对应记录状态为Rollbacked,然后释放全局锁。

branchRollback(globalSession, branchSession)方法里面会调用到AbstractCore#branchRollback(GlobalSession globalSession, BranchSession branchSession)方法

AbstractCore#branchRollback(GlobalSession globalSession, BranchSession branchSession)

java 复制代码
    @Override
    public BranchStatus branchRollback(GlobalSession globalSession, BranchSession branchSession) throws TransactionException {
        try {
        	// 请求对象类型为BranchRollbackRequest
            BranchRollbackRequest request = new BranchRollbackRequest();
            request.setXid(branchSession.getXid()); // 设置全局事务id
            request.setBranchId(branchSession.getBranchId()); // 设置分支事务id
            request.setResourceId(branchSession.getResourceId());
            request.setApplicationData(branchSession.getApplicationData());
            request.setBranchType(branchSession.getBranchType());
            // 里面就是通过Netty发起rpc远程调用,通知RM进行分支事务回滚
            return branchRollbackSend(request, globalSession, branchSession);
        } catch () {}
    }

最后就是通过Netty发起rpc远程调用,通知RM进行分支事务回滚。

四、RM处理分支事务回滚:AbstractRMHandler#handle(BranchRollbackRequest)

RM接收到TC的分支事务回滚请求时,会调用AbstractRMHandler#handle(BranchRollbackRequest)方法进行处理。

java 复制代码
    @Override
    public BranchRollbackResponse handle(BranchRollbackRequest request) {
        BranchRollbackResponse response = new BranchRollbackResponse();
        exceptionHandleTemplate(new AbstractCallback<BranchRollbackRequest, BranchRollbackResponse>() {
            @Override
            public void execute(BranchRollbackRequest request, BranchRollbackResponse response)
                throws TransactionException {
                // 处理分支事务回滚
                doBranchRollback(request, response);
            }
        }, request, response);
        return response;
    }

AbstractRMHandler#doBranchRollback(BranchRollbackRequest request, BranchRollbackResponse response)

java 复制代码
    protected void doBranchRollback(BranchRollbackRequest request, BranchRollbackResponse response)
        throws TransactionException {
        String xid = request.getXid();
        long branchId = request.getBranchId();
        String resourceId = request.getResourceId();
        String applicationData = request.getApplicationData();
        // 处理分支事务回滚
        BranchStatus status = getResourceManager().branchRollback(request.getBranchType(), xid, branchId, resourceId,
            applicationData);
        response.setXid(xid);
        response.setBranchId(branchId);
        response.setBranchStatus(status);
    }

DataSourceManager#branchRollback(...)

java 复制代码
    @Override
    public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId,
                                       String applicationData) throws TransactionException {
        DataSourceProxy dataSourceProxy = get(resourceId);
        try {
            // 调用UndoLogManager的undo方法,查询undo_log表对应记录,生成回滚sql并执行
            UndoLogManagerFactory.getUndoLogManager(dataSourceProxy.getDbType()).undo(dataSourceProxy, xid, branchId);
        } catch () {}
        return BranchStatus.PhaseTwo_Rollbacked;

    }

最后就是调用UndoLogManager的undo方法,查询undo_log表对应的记录,解析出回滚sql,然后执行回滚sql进行反向补偿。

相关推荐
重铸码农荣光2 小时前
🎯 从零搭建一个 React Todo 应用:父子通信、状态管理与本地持久化全解析!
前端·react.js·架构
用户4099322502122 小时前
Vue3 v-if与v-show:销毁还是隐藏,如何抉择?
前端·vue.js·后端
派大鑫wink2 小时前
【Day12】String 类详解:不可变性、常用方法与字符串拼接优化
java·开发语言
Java编程爱好者2 小时前
SpringBoot启动太慢?几个优化技巧
后端
喷火龙8号2 小时前
修复 Hertz + OpenTelemetry 链路追踪中的数据竞争问题
后端
柏木乃一2 小时前
进程(6)进程切换,Linux中的进程组织,Linux进程调度算法
linux·服务器·c++·算法·架构·操作系统
JIngJaneIL2 小时前
基于springboot + vue健康管理系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·后端
程序员小胖2 小时前
每天一道面试题之架构篇|Java 热部署插件化架构设计
后端
秋饼2 小时前
【三大锁王争霸赛:Java锁、数据库锁、分布式锁谁是卷王?】
java·数据库·分布式