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

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

当调用链一切顺利,没有发生异常时,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 {}

commitTransaction(GlobalTransaction tx)

java 复制代码
    private void commitTransaction(GlobalTransaction tx) throws TransactionalExecutor.ExecutionException {
        try {
        	// 进入DefaultGlobalTransaction#commit()
            tx.commit();
        } catch () {}
    }

commitTransaction(GlobalTransaction tx)会调用DefaultGlobalTransaction的commit()方法,里面会进行全局事务提交。

DefaultGlobalTransaction#commit()

java 复制代码
    @Override
    public void commit() throws TransactionException {
         
        try {
        	// 提交失败可以重试
            while (retry > 0) {
                try {
                	// 调用TransactionManager的commit方法提交全局事务
                    status = transactionManager.commit(xid);
                    break;
                } catch () {
                    retry--;
                }
            }
        } finally {}
        
    }

DefaultTransactionManager#commit(String xid)

java 复制代码
    @Override
    public GlobalStatus commit(String xid) throws TransactionException {
    	// 创建全局事务提交的请求对象
        GlobalCommitRequest globalCommit = new GlobalCommitRequest();
        globalCommit.setXid(xid);
        // 请求TC提交全局事务
        GlobalCommitResponse response = (GlobalCommitResponse) syncCall(globalCommit);
        return response.getGlobalStatus();
    }
java 复制代码
    private AbstractTransactionResponse syncCall(AbstractTransactionRequest request) throws TransactionException {
        try {
        	// 使用Netty发起请求
            return (AbstractTransactionResponse) TmNettyRemotingClient.getInstance().sendSyncRequest(request);
        } catch () {}
    }

syncCall方法里面就是通过Netty向TC发起请求。

TC接收到请求后会调用DefaultCoordinator#doGlobalCommit(GlobalCommitRequest request, ...)方法处理请求。

DefaultCoordinator#doGlobalCommit(GlobalCommitRequest request, ...)

java 复制代码
    @Override
    protected void doGlobalCommit(GlobalCommitRequest request, GlobalCommitResponse response, RpcContext rpcContext)
        throws TransactionException {
        // 调用DefaultCode的commit方法处理全局事务提交请求        
        response.setGlobalStatus(core.commit(request.getXid()));
    }

DefaultCore#commit(String xid)

java 复制代码
    @Override
    public GlobalStatus commit(String xid) throws TransactionException {
    	// 根据xid查询GlobalSession
        GlobalSession globalSession = SessionHolder.findGlobalSession(xid);
        if (globalSession == null) {
        	// 如果GlobalSession为null,返回Finished状态
            return GlobalStatus.Finished;
        }


        boolean shouldCommit = SessionHolder.lockAndExecute(globalSession, () -> {
            // GlobalSession设置为非活跃,并释放全局锁
            globalSession.closeAndClean();
            if (globalSession.getStatus() == GlobalStatus.Begin) {
            	// 如果AT模式,会进入这个分支,设置GlobalSession状态为AsyncCommitting
                if (globalSession.canBeCommittedAsync()) {
                    globalSession.asyncCommit();
                    // shouldCommit为false
                    return false;
                } else {
                    globalSession.changeStatus(GlobalStatus.Committing);
                    return true;
                }
            }
            return false;
        });

        if (shouldCommit) {
        	// 同步提交的逻辑,AT模式不会进这里,忽略...
        } else {
        	// 返回Committed状态
            return globalSession.getStatus() == GlobalStatus.AsyncCommitting ? GlobalStatus.Committed : globalSession.getStatus();
        }
    }

AT模式下,不会进行同步提交。DefaultCore#commit(String xid)方法在AT模式下干的事情处理释放全局锁以外,仅仅就是把GlobalSession的状态改为AsyncCommitting也就没了。

GlobalSession被修改为AsyncCommitting状态后,会有定时任务对它们进行后续的处理。

DefaultCoordinator#init()

java 复制代码
    public void init() {

        asyncCommitting.scheduleAtFixedRate(() -> {
            boolean lock = SessionHolder.acquireDistributedLock(ASYNC_COMMITTING);
            if (lock) {
                try {
                	// 定时任务处理AsyncCommitting状态的GlobalSession
                    handleAsyncCommitting();
                } catch () {} finally {
                    SessionHolder.releaseDistributedLock(ASYNC_COMMITTING);
                }
            }
        }, 0, ASYNC_COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS);

    }

下面我们单独看看handleAsyncCommitting()方法。

handleAsyncCommitting()

java 复制代码
    protected void handleAsyncCommitting() {
    	// 查询当前所有状态为AsyncCommitting的GlobalSession
        Collection<GlobalSession> asyncCommittingSessions = SessionHolder.getAsyncCommittingSessionManager()
            .allSessions();
        SessionHelper.forEach(asyncCommittingSessions, asyncCommittingSession -> {
            try {
            	// 调用DefaultCore的doGlobalCommit方法
                core.doGlobalCommit(asyncCommittingSession, true);
            } catch () {}
        });
    }

handleAsyncCommitting()方法会查询所有状态为AsyncCommitting的GlobalSession,然后遍历每一个GlobalSession,调用DefaultCore的doGlobalCommit方法进行处理。

DefaultCore#doGlobalCommit(GlobalSession globalSession, boolean retrying)

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

        if (globalSession.isSaga()) {
            // saga模式的处理逻辑,忽略
        } else {
        	// 遍历分支事务信息branchSession 进行处理
            Boolean result = SessionHelper.forEach(globalSession.getSortedBranches(), branchSession -> {

                BranchStatus currentStatus = branchSession.getStatus();
                if (currentStatus == BranchStatus.PhaseOne_Failed) {
                	// 删除一阶段失败的分支事务
                    globalSession.removeBranch(branchSession);
                    return CONTINUE;
                }
                try {
                	// 发送请求通知该分支事务对应的RM提交分支事务
                    BranchStatus branchStatus = getCore(branchSession.getBranchType()).branchCommit(globalSession, branchSession);

                    switch (branchStatus) {
                        case PhaseTwo_Committed:
                        	// RM返回提交成功,删除分支事务
                            globalSession.removeBranch(branchSession);
                            return CONTINUE;
                        case PhaseTwo_CommitFailed_Unretryable:
                        default:
                    }
                } catch () {}
                return CONTINUE;
            });
        }
        
        if (success && globalSession.getBranchSessions().isEmpty() && retrying) {
        	// 修改GlobalSession状态为Committed
            SessionHelper.endCommitted(globalSession);
        }
        return success;
    }

getCore(branchSession.getBranchType()).branchCommit(globalSession, branchSession)里面会发送请求通知RM进行分支事务提交。

java 复制代码
    public BranchStatus branchCommit(GlobalSession globalSession, BranchSession branchSession) throws TransactionException {
        try {
        	// 构建请求对象
            BranchCommitRequest request = new BranchCommitRequest();
            request.setXid(branchSession.getXid()); // 全局事务id
            request.setBranchId(branchSession.getBranchId()); // 分支事务id
            request.setResourceId(branchSession.getResourceId());
            request.setApplicationData(branchSession.getApplicationData());
            request.setBranchType(branchSession.getBranchType());
            // 发送请求通知RM进行分支事务提交
            return branchCommitSend(request, globalSession, branchSession);
        } catch () {}
    }
java 复制代码
    protected BranchStatus branchCommitSend(BranchCommitRequest request, GlobalSession globalSession,
                                            BranchSession branchSession) throws IOException, TimeoutException {
		// 使用netty发送请求通知RM进行分支事务提交
        BranchCommitResponse response = (BranchCommitResponse) remotingServer.sendSyncRequest(
                branchSession.getResourceId(), branchSession.getClientId(), request);
        return response.getBranchStatus();
    }

最终通过netty发送请求。

AbstractRMHandler#handle(BranchCommitRequest)

RM接收到请求后,会进入AbstractRMHandler#handle(BranchCommitRequest)方法。

java 复制代码
    @Override
    public BranchCommitResponse handle(BranchCommitRequest request) {
        BranchCommitResponse response = new BranchCommitResponse();
        exceptionHandleTemplate(new AbstractCallback<BranchCommitRequest, BranchCommitResponse>() {
            @Override
            public void execute(BranchCommitRequest request, BranchCommitResponse response)
                throws TransactionException {
                // RM进行分支事务提交的处理
                doBranchCommit(request, response);
            }
        }, request, response);
        return response;
    }
java 复制代码
    protected void doBranchCommit(BranchCommitRequest request, BranchCommitResponse response)
        throws TransactionException {
        String xid = request.getXid();
        long branchId = request.getBranchId();
        String resourceId = request.getResourceId();
        String applicationData = request.getApplicationData();
        // 根据xid和branchId提交分支事务
        BranchStatus status = getResourceManager().branchCommit(request.getBranchType(), xid, branchId, resourceId,
            applicationData);
        response.setXid(xid);
        response.setBranchId(branchId);
        response.setBranchStatus(status);

    }
java 复制代码
    public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId,
                                     String applicationData) throws TransactionException {
		// 往AsyncWorker提交了一个任务,异步进行提交                                     
        return asyncWorker.branchCommit(xid, branchId, resourceId);
    }

调用asyncWorker的branchCommit方法,里面就是提交一个任务到队列。

java 复制代码
    public BranchStatus branchCommit(String xid, long branchId, String resourceId) {
        Phase2Context context = new Phase2Context(xid, branchId, resourceId);
        // xid、branchId、resourceId三元包装成Phase2Context,添加到队列队列
        addToCommitQueue(context);
        return BranchStatus.PhaseTwo_Committed;
    }

asyncWorker.branchCommit(xid, branchId, resourceId)方法添加任务到队列后,会返回PhaseTwo_Committed表示提交成功。

AsyncWorker

AsyncWorker的构造方法会启动定时任务,处理队列中的任务。

java 复制代码
    public AsyncWorker(DataSourceManager dataSourceManager) {
    	scheduledExecutor = new ScheduledThreadPoolExecutor(2, threadFactory);
        // 启动定时任务
        scheduledExecutor.scheduleAtFixedRate(this::doBranchCommitSafely, 10, 1000, TimeUnit.MILLISECONDS);
    }

定时任务调用doBranchCommitSafely方法。

java 复制代码
    void doBranchCommitSafely() {
        try {
            doBranchCommit();
        } catch () {}
    }
java 复制代码
    private void doBranchCommit() {
        // 取出队列中所有的Phase2Context(也就是前面添加进去的任务)
        List<Phase2Context> allContexts = new LinkedList<>();
        commitQueue.drainTo(allContexts);

        // 按resourceId分组 
        Map<String, List<Phase2Context>> groupedContexts = groupedByResourceId(allContexts);
		
		// 遍历groupedContexts,调用dealWithGroupedContexts方法
        groupedContexts.forEach(this::dealWithGroupedContexts);
    }
java 复制代码
    private void dealWithGroupedContexts(String resourceId, List<Phase2Context> contexts) {
        DataSourceProxy dataSourceProxy = dataSourceManager.get(resourceId);
        
        Connection conn;
        try {
            conn = dataSourceProxy.getPlainConnection();
        } catch () {}

        UndoLogManager undoLogManager = UndoLogManagerFactory.getUndoLogManager(dataSourceProxy.getDbType());

        List<List<Phase2Context>> splitByLimit = Lists.partition(contexts, UNDOLOG_DELETE_LIMIT_SIZE);
        // 对分组下的Phase2Context进行分批,遍历每一批,批量删除undolog记录
        splitByLimit.forEach(partition -> deleteUndoLog(conn, undoLogManager, partition));
    }

所以异步操作只是做了一个删除undolog表记录的操作,其他啥也没干。

相关推荐
JAVA+C语言2 小时前
String Constant Pool
java·开发语言
快乐肚皮2 小时前
一文了解XSS攻击:分类、原理与全方位防御方案
java·前端·xss
白宇横流学长3 小时前
基于SpringBoot实现的历史馆藏系统设计与实现【源码+文档】
java·spring boot·后端
moxiaoran57533 小时前
Go语言结构体
开发语言·后端·golang
毕设源码-赖学姐3 小时前
【开题答辩全过程】以 高校教学资源共享系统的设计与实现为例,包含答辩的问题和答案
java·eclipse
爱海贼的无处不在3 小时前
现在还有Java面试者不会开发Starter组件
后端·面试·架构
珠海西格电力3 小时前
零碳园区物流园区架构协同方案
人工智能·物联网·架构·能源
辣机小司3 小时前
【软件设计师】自编思维导图和学习资料分享(中级已过)
java·c++·软考·软件设计师
爱笑的眼睛114 小时前
深入解析Matplotlib Axes API:构建复杂可视化架构的核心
java·人工智能·python·ai