【深入理解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 小时前
【成长类】《只有偏执狂才能生存》读书笔记:程序员的偏执型成长地图
后端
0xDevNull2 小时前
MySQL数据冷热分离详解
后端·mysql
一定要AK2 小时前
Spring 入门核心笔记
java·笔记·spring
A__tao2 小时前
Elasticsearch Mapping 一键生成 Java 实体类(支持嵌套 + 自动过滤注释)
java·python·elasticsearch
AI袋鼠帝2 小时前
OpenClaw(龙虾)最强开源对手!Github 40K Star了,又一个爆火的Agent..
后端
KevinCyao2 小时前
java视频短信接口怎么调用?SpringBoot集成视频短信及回调处理Demo
java·spring boot·音视频
科技小花2 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
凯尔萨厮2 小时前
创建SpringWeb项目(Spring2.0)
spring·mvc·mybatis
2501_948114242 小时前
2026年大模型API聚合平台技术评测:企业级接入层的治理演进与星链4SAPI架构观察
大数据·人工智能·gpt·架构·claude
迷藏4943 小时前
**发散创新:基于Rust实现的开源合规权限管理框架设计与实践**在现代软件架构中,**权限控制(RBAC)** 已成为保障
java·开发语言·python·rust·开源