seata-二阶段提交-笔记4

一阶段前三篇:seata 2阶段提交实现代码-笔记1-CSDN博客

seata 2阶段提交实现代码-笔记2-CSDN博客

seata-2阶段提交-笔记3-CSDN博客

回归下AT模式:

两阶段提交协议的演变:

  • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
  • 二阶段:
    • 提交异步化,非常快速地完成。
    • 回滚通过一阶段的回滚日志进行反向补偿。

二阶段-提交

篇1整理过transactionalTemplate.execute方法,就是一阶段执行完成后如果没有业务异常,TM会向TC提交全局事务请求。

复制代码
// 4. everything is fine, commit.
commitTransaction(tx, txInfo);

内部会执行 tx.commit();

>> io.seata.tm.api.DefaultGlobalTransaction#commit()

java 复制代码
    @Override
    public void commit() throws TransactionException {
        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();
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("transaction {} will be commit", xid);
        }
        int retry = COMMIT_RETRY_COUNT <= 0 ? DEFAULT_TM_COMMIT_RETRY_COUNT : COMMIT_RETRY_COUNT;
        try {
            while (retry > 0) {
                try {
                    retry--;
                    status = transactionManager.commit(xid);
                    break;
                } catch (Throwable ex) {
                    LOGGER.error("Failed to report global commit [{}],Retry Countdown: {}, reason: {}", this.getXid(), retry, ex.getMessage());
                    if (retry == 0) {
                        throw new TransactionException("Failed to report global commit", ex);
                    }
                }
            }
        } finally {
            if (xid.equals(RootContext.getXID())) {
                suspend(true);
            }
        }
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("[{}] commit status: {}", xid, status);
        }
    }

这个主要逻辑:1 判断角色,是TM才会执行 。

2 重复发起调用TC发起事务提交请求,最多5次。

复制代码
 transactionManager.commit() 底层也是netty的 请求GlobalCommitRequest,返回:GlobalCommitResponse

TC的处理全局事务提交

server 对于GlobalCommitRequest 请求处理逻辑在

io.seata.server.coordinator.DefaultCoordinator#doGlobalCommit

java 复制代码
    @Override
    protected void doGlobalCommit(GlobalCommitRequest request, GlobalCommitResponse response, RpcContext rpcContext)
            throws TransactionException {
        MDC.put(RootContext.MDC_KEY_XID, request.getXid());
        response.setGlobalStatus(core.commit(request.getXid()));//设置全局事务状态
    }

接下来看看commit 方法,其中AT模式是异步提交。

java 复制代码
 @Override
    public GlobalStatus commit(String xid) throws TransactionException {
        //根据xid获取全局事务对象GlobalSession,db模式下会查询global_table表
        GlobalSession globalSession = SessionHolder.findGlobalSession(xid);
        if (globalSession == null) {//为空直接返回finished状态
            return GlobalStatus.Finished;
        }

        if (globalSession.isTimeout()) {
            LOGGER.info("TC detected timeout, xid = {}", globalSession.getXid());
            return GlobalStatus.TimeoutRollbacking;
        }

        // just lock changeStatus
        boolean shouldCommit = SessionHolder.lockAndExecute(globalSession, () -> {
            boolean shouldCommitNow = false;
            if (globalSession.getStatus() == GlobalStatus.Begin) {
                // Highlight: Firstly, close the session, then no more branch can be registered.
                //关闭,防止有新的branch注册进来
                globalSession.close();
                if (globalSession.canBeCommittedAsync()) {//只有AT可以异步提交
                    globalSession.asyncCommit();
                    MetricsPublisher.postSessionDoneEvent(globalSession, GlobalStatus.Committed, false, false);
                } else {
                    globalSession.changeGlobalStatus(GlobalStatus.Committing);
                    shouldCommitNow = true;
                }
                //clean session after changing status successfully.
                globalSession.clean();
            }
            return shouldCommitNow;
        });

        if (shouldCommit) {//同步提交 TCC
            boolean success = doGlobalCommit(globalSession, false);
            //If successful and all remaining branches can be committed asynchronously, do async commit.
            if (success && globalSession.hasBranch() && globalSession.canBeCommittedAsync()) {
                globalSession.asyncCommit();
                return GlobalStatus.Committed;
            } else {
                return globalSession.getStatus();
            }
        } else {
            return globalSession.getStatus() == GlobalStatus.AsyncCommitting ? GlobalStatus.Committed : globalSession.getStatus();
        }
    }
复制代码
在看看异步提交
复制代码
public void asyncCommit() throws TransactionException {
    changeGlobalStatus(GlobalStatus.AsyncCommitting);
}
java 复制代码
    public void changeGlobalStatus(GlobalStatus status) throws TransactionException {
        if (GlobalStatus.Rollbacking == status || GlobalStatus.TimeoutRollbacking == status) {
            LockerManagerFactory.getLockManager().updateLockStatus(xid, LockStatus.Rollbacking);
        }
        SessionHolder.getRootSessionManager().onStatusChange(this, status);
        // set session status after update successfully
        this.status = status;
        for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
            lifecycleListener.onStatusChange(this, status);
        }
    }

更新:global_table 表状态

可以看到AT模式异步提交只是更新了状态,实际执行在哪里呢?io.seata.server.coordinator.DefaultCoordinator#init,里面指定了线程池,每隔1秒执行

java 复制代码
    public void init() {
        retryRollbacking.scheduleAtFixedRate(
            () -> SessionHolder.distributedLockAndExecute(RETRY_ROLLBACKING, this::handleRetryRollbacking), 0,
            ROLLBACKING_RETRY_PERIOD, TimeUnit.MILLISECONDS);

        retryCommitting.scheduleAtFixedRate(
            () -> SessionHolder.distributedLockAndExecute(RETRY_COMMITTING, this::handleRetryCommitting), 0,
            COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS);
        //异步提交在这里处理
        asyncCommitting.scheduleAtFixedRate(
            () -> SessionHolder.distributedLockAndExecute(ASYNC_COMMITTING, this::handleAsyncCommitting), 0,
            ASYNC_COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS);

        timeoutCheck.scheduleAtFixedRate(
            () -> SessionHolder.distributedLockAndExecute(TX_TIMEOUT_CHECK, this::timeoutCheck), 0,
            TIMEOUT_RETRY_PERIOD, TimeUnit.MILLISECONDS);

        undoLogDelete.scheduleAtFixedRate(
            () -> SessionHolder.distributedLockAndExecute(UNDOLOG_DELETE, this::undoLogDelete),
            UNDO_LOG_DELAY_DELETE_PERIOD, UNDO_LOG_DELETE_PERIOD, TimeUnit.MILLISECONDS);
    }

这个distributedLockAndExecute先要获取分布式锁。

java 复制代码
    /**
     * Execute the function after get the distribute lock
     *
     * @param key  the distribute lock key
     * @param func the function to be call
     * @return whether the func be call
     */
    public static boolean distributedLockAndExecute(String key, NoArgsFunc func) {
        boolean lock = false;
        try {
            if (lock = acquireDistributedLock(key)) {
                func.call();
            }
        } catch (Exception e) {
            LOGGER.error("Exception running function with key = {}", key, e);
        } finally {
            if (lock) {
                try {
                    SessionHolder.releaseDistributedLock(key);
                } catch (Exception ex) {
                    LOGGER.warn("release distribute lock failure, message = {}", ex.getMessage(), ex);
                }
            }
        }
        return lock;
    }

底层依赖seata库的distributed_lock表。再看看具体实现handleAsyncCommitting

java 复制代码
    /**
     * Handle async committing.
     */
    protected void handleAsyncCommitting() {
       //获取AsyncCommiting状态GlobalSession
        SessionCondition sessionCondition = new SessionCondition(GlobalStatus.AsyncCommitting);
        Collection<GlobalSession> asyncCommittingSessions =
                SessionHolder.getRootSessionManager().findGlobalSessions(sessionCondition);
        if (CollectionUtils.isEmpty(asyncCommittingSessions)) {
            return;
        }
        SessionHelper.forEach(asyncCommittingSessions, asyncCommittingSession -> {
            try {//处理
                core.doGlobalCommit(asyncCommittingSession, true);
            } catch (TransactionException ex) {
                LOGGER.error("Failed to async committing [{}] {} {}", asyncCommittingSession.getXid(), ex.getCode(), ex.getMessage(), ex);
            }
        });
    }

还是调用了同步的方法core.doGlobalCommit。

java 复制代码
 public boolean doGlobalCommit(GlobalSession globalSession, boolean retrying) throws TransactionException {
        boolean success = true;
        // start committing event  发布事件
        MetricsPublisher.postSessionDoingEvent(globalSession, retrying);

        if (globalSession.isSaga()) {
            success = getCore(BranchType.SAGA).doGlobalCommit(globalSession, retrying);
        } else {// 获取全部的分支事务
            List<BranchSession> branchSessions = globalSession.getSortedBranches();
            Boolean result = SessionHelper.forEach(branchSessions, branchSession -> {
                // if not retrying, skip the canBeCommittedAsync branches
                if (!retrying && branchSession.canBeCommittedAsync()) {
                    return CONTINUE;
                }

                BranchStatus currentStatus = branchSession.getStatus();
                if (currentStatus == BranchStatus.PhaseOne_Failed) {//一阶段失败,删除分支
                    SessionHelper.removeBranch(globalSession, branchSession, !retrying);
                    return CONTINUE;
                }
                try {//发送rpc 通知RM删除undo_log
                    BranchStatus branchStatus = getCore(branchSession.getBranchType()).branchCommit(globalSession, branchSession);
                    if (isXaerNotaTimeout(globalSession,branchStatus)) {
                        LOGGER.info("Commit branch XAER_NOTA retry timeout, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId());
                        branchStatus = BranchStatus.PhaseTwo_Committed;
                    }
                    switch (branchStatus) {
                        case PhaseTwo_Committed:
                            SessionHelper.removeBranch(globalSession, branchSession, !retrying);
                            LOGGER.info("Commit branch transaction successfully, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId());
                            return CONTINUE;
                        case PhaseTwo_CommitFailed_Unretryable:
                            //not at branch
                            SessionHelper.endCommitFailed(globalSession, retrying);
                            LOGGER.error("Committing global transaction[{}] finally failed, caused by branch transaction[{}] commit failed.", globalSession.getXid(), branchSession.getBranchId());
                            return false;

                        default:
                            if (!retrying) {//当前是否重试,
                                globalSession.queueToRetryCommit();
                                return false;
                            }
                            if (globalSession.canBeCommittedAsync()) {
                                LOGGER.error("Committing branch transaction[{}], status:{} and will retry later",
                                    branchSession.getBranchId(), branchStatus);
                                return CONTINUE;
                            } else {
                                LOGGER.error(
                                    "Committing global transaction[{}] failed, caused by branch transaction[{}] commit failed, will retry later.", globalSession.getXid(), branchSession.getBranchId());
                                return false;
                            }
                    }
                } catch (Exception ex) {
                    String commitInfo = retrying ? "Global commit continue" : "Global commit failed";
                    StackTraceLogger.error(LOGGER, ex, "Committing branch transaction exception:retrying={}, {}, {}",
                        new String[] {String.valueOf(retrying), branchSession.toString(), commitInfo});
                    if (!retrying) {
                        globalSession.queueToRetryCommit();
                        throw new TransactionException(ex);
                    }
                }
                return CONTINUE;
            }, PARALLEL_HANDLE_BRANCH && branchSessions.size() >= 2);
            // Return if the result is not null
            if (result != null) {
                return result;
            }
            //If has branch and not all remaining branches can be committed asynchronously,
            //do print log and return false  有分支事务,且不能异步提交,说明失败了
            if (globalSession.hasBranch() && !globalSession.canBeCommittedAsync()) {
                LOGGER.info("Committing global transaction is NOT done, xid = {}.", globalSession.getXid());
                return false;
            }
        }
        // if it succeeds and there is no branch, retrying=true is the asynchronous state when retrying. EndCommitted is
        // executed to improve concurrency performance, and the global transaction ends..  分支事务全部提交成功了
        if (success && globalSession.getBranchSessions().isEmpty()) {
            SessionHelper.endCommitted(globalSession, retrying);//删除全局事务信息
            LOGGER.info("Committing global transaction is successfully done, xid = {}.", globalSession.getXid());
        }
        return success;
    }

全局事务的提交,会遍历所有与之关联的分支事务ID,向RM发送BranchCommitRequest 消息,RM分支事务提交成功后将分支事务从全局事务中删除,删除seata库分支事务表branch_table的数据。当所有分支事务全部执行成功后,将GlobalSession信息从数据库中global_table删除

下面看下里面的关键步骤:

1 TC通知RM

java 复制代码
    public BranchStatus branchCommit(GlobalSession globalSession, BranchSession branchSession) throws TransactionException {
        try {
            BranchCommitRequest request = new BranchCommitRequest();
            request.setXid(branchSession.getXid());
            request.setBranchId(branchSession.getBranchId());
            request.setResourceId(branchSession.getResourceId());
            request.setApplicationData(branchSession.getApplicationData());
            request.setBranchType(branchSession.getBranchType());
            return branchCommitSend(request, globalSession, branchSession);
        } catch (IOException | TimeoutException e) {
            throw new BranchTransactionException(FailedToSendBranchCommitRequest,
                    String.format("Send branch commit failed, xid = %s branchId = %s", branchSession.getXid(),
                            branchSession.getBranchId()), e);
        }
    }

2 TC 分支事务从全局事务中删除,删除seata库分支事务表branch_table的数据。

java 复制代码
    public static void removeBranch(GlobalSession globalSession, BranchSession branchSession, boolean isAsync)
            throws TransactionException {
        globalSession.unlockBranch(branchSession);
        if (isEnableBranchRemoveAsync() && isAsync) {
            COORDINATOR.doBranchRemoveAsync(globalSession, branchSession);
        } else {
            globalSession.removeBranch(branchSession);
        }
    }

其中异步删除调用io.seata.server.coordinator.DefaultCoordinator.BranchRemoveTask 来删

,会删除全局锁。

RM提交分支事务

TC发送BranchCommitRequest消息后,RM接收到并将消息交由RmBranchCommitProcessor处理

再io.seata.core.rpc.netty.RmNettyRemotingClient#registerProcessor 注册了

java 复制代码
    private void registerProcessor() {
        // 1.registry rm client handle branch commit processor
        RmBranchCommitProcessor rmBranchCommitProcessor = new RmBranchCommitProcessor(getTransactionMessageHandler(), this);
        super.registerProcessor(MessageType.TYPE_BRANCH_COMMIT, rmBranchCommitProcessor, messageExecutor);
        // 2.registry rm client handle branch rollback processor
        RmBranchRollbackProcessor rmBranchRollbackProcessor = new RmBranchRollbackProcessor(getTransactionMessageHandler(), this);
        super.registerProcessor(MessageType.TYPE_BRANCH_ROLLBACK, rmBranchRollbackProcessor, messageExecutor);
        // 3.registry rm handler undo log processor
        RmUndoLogProcessor rmUndoLogProcessor = new RmUndoLogProcessor(getTransactionMessageHandler());
        super.registerProcessor(MessageType.TYPE_RM_DELETE_UNDOLOG, rmUndoLogProcessor, messageExecutor);
        // 4.registry TC response processor
        ClientOnResponseProcessor onResponseProcessor =
            new ClientOnResponseProcessor(mergeMsgMap, super.getFutures(), getTransactionMessageHandler());
        super.registerProcessor(MessageType.TYPE_SEATA_MERGE_RESULT, onResponseProcessor, null);
        super.registerProcessor(MessageType.TYPE_BRANCH_REGISTER_RESULT, onResponseProcessor, null);
        super.registerProcessor(MessageType.TYPE_BRANCH_STATUS_REPORT_RESULT, onResponseProcessor, null);
        super.registerProcessor(MessageType.TYPE_GLOBAL_LOCK_QUERY_RESULT, onResponseProcessor, null);
        super.registerProcessor(MessageType.TYPE_REG_RM_RESULT, onResponseProcessor, null);
        super.registerProcessor(MessageType.TYPE_BATCH_RESULT_MSG, onResponseProcessor, null);
        // 5.registry heartbeat message processor
        ClientHeartbeatProcessor clientHeartbeatProcessor = new ClientHeartbeatProcessor();
        super.registerProcessor(MessageType.TYPE_HEARTBEAT_MSG, clientHeartbeatProcessor, null);
    }

io.seata.core.rpc.processor.client.RmBranchCommitProcessor#process

io.seata.core.rpc.processor.client.RmBranchCommitProcessor#handleBranchCommit

io.seata.rm.AbstractRMHandler#onRequest

io.seata.rm.AbstractRMHandler#handle(io.seata.core.protocol.transaction.BranchCommitRequest)

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();
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Branch committing: " + xid + " " + branchId + " " + resourceId + " " + applicationData);
        }//真正的处理
        BranchStatus status = getResourceManager().branchCommit(request.getBranchType(), xid, branchId, resourceId,
            applicationData);
        response.setXid(xid);
        response.setBranchId(branchId);
        response.setBranchStatus(status);
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Branch commit result: " + status);
        }

io.seata.rm.datasource.DataSourceManager#branchCommit

java 复制代码
    public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId,
                                     String applicationData) throws TransactionException {
        return asyncWorker.branchCommit(xid, branchId, resourceId);
    }

io.seata.rm.datasource.AsyncWorker#branchCommit

java 复制代码
    public BranchStatus branchCommit(String xid, long branchId, String resourceId) {
        Phase2Context context = new Phase2Context(xid, branchId, resourceId);
        addToCommitQueue(context);//加入提交队列
        return BranchStatus.PhaseTwo_Committed;
    }

我们看下AsyncWorker的构造方法,也是有任务队列,每1秒执行

java 复制代码
    public AsyncWorker(DataSourceManager dataSourceManager) {
        this.dataSourceManager = dataSourceManager;

        LOGGER.info("Async Commit Buffer Limit: {}", ASYNC_COMMIT_BUFFER_LIMIT);
        commitQueue = new LinkedBlockingQueue<>(ASYNC_COMMIT_BUFFER_LIMIT);

        ThreadFactory threadFactory = new NamedThreadFactory("AsyncWorker", 2, true);
        scheduledExecutor = new ScheduledThreadPoolExecutor(2, threadFactory);
        scheduledExecutor.scheduleAtFixedRate(this::doBranchCommitSafely, 10, 1000, TimeUnit.MILLISECONDS);
    }
java 复制代码
    private void doBranchCommit() {
        if (commitQueue.isEmpty()) {
            return;
        }

        // transfer all context currently received to this list
        List<Phase2Context> allContexts = new LinkedList<>();
        commitQueue.drainTo(allContexts);

        // group context by their resourceId
        Map<String, List<Phase2Context>> groupedContexts = groupedByResourceId(allContexts);

        groupedContexts.forEach(this::dealWithGroupedContexts);
    }
java 复制代码
 private void dealWithGroupedContexts(String resourceId, List<Phase2Context> contexts) {
        if (StringUtils.isBlank(resourceId)) {
            //ConcurrentHashMap required notNull key
            LOGGER.warn("resourceId is empty and will skip.");
            return;
        }
        DataSourceProxy dataSourceProxy = dataSourceManager.get(resourceId);
        if (dataSourceProxy == null) {
            LOGGER.warn("failed to find resource for {} and requeue", resourceId);
            addAllToCommitQueue(contexts);
            return;
        }

        Connection conn = null;
        try {
            conn = dataSourceProxy.getPlainConnection();
            UndoLogManager undoLogManager = UndoLogManagerFactory.getUndoLogManager(dataSourceProxy.getDbType());

            // split contexts into several lists, with each list contain no more element than limit size
            List<List<Phase2Context>> splitByLimit = Lists.partition(contexts, UNDOLOG_DELETE_LIMIT_SIZE);
            for (List<Phase2Context> partition : splitByLimit) {
                deleteUndoLog(conn, undoLogManager, partition);//删除undologr日志
            }
        } catch (SQLException sqlExx) {
            addAllToCommitQueue(contexts);
            LOGGER.error("failed to get connection for async committing on {} and requeue", resourceId, sqlExx);
        } finally {
            IOUtil.close(conn);
        }

    }

小结:

AT 模式下一阶段结束后,TM向TC发起全局事务提交请求。

TC收到后异步删除全局事务、分支事务、释放全局锁,并通知RM删除undo_log

先整理到这里,回滚的待后续整理。

相关推荐
m0_689618285 小时前
敏捷多模态微型机器人:独特的被动变形轮设计
笔记·机器人
-self-disciplinese6 小时前
从零开始学Java,学习笔记Day24
java·笔记·后端·学习
Sthamansa8 小时前
Java学习笔记(13)——面向对象编程
java·笔记·学习
qq_430583978 小时前
QT笔记- QClipboard剪切板对QByteArray数据的复制与粘贴
开发语言·笔记·qt
美式小田8 小时前
Cadence学习笔记 8 添加分页符
笔记·嵌入式硬件·学习·cadence
14_118 小时前
Cherno C++学习笔记 P43 对象生存周期
c++·笔记·学习
美式小田9 小时前
Cadence学习笔记 9 器件封装匹配
笔记·嵌入式硬件·学习·cadence
小风来临的时候9 小时前
手机银行模拟器,一款高仿真银行app的模拟器,可以修改姓名 卡号 余额 做转账记录 做流水
笔记·百度·微信小程序·小程序·百度小程序
考试宝9 小时前
道路运输企业安全生产管理人员安全考核试题
网络·人工智能·笔记·安全·职场和发展·学习方法·业界资讯