Seata原始碼分析——SessionManager

飛僧明天起飛發表於2022-05-30


我們知道Seata服務端TC在全域性事務中需要協調TM,RM分工幹活,一個全域性事務的也是由多個分支事務組成的,那麼TC端必須要對這些全域性事務和分支事務進行管理,比如事務的建立、更新、刪除...我們今天就來聊一聊Seata中的事務管理者SessionManager。

*這裡為什麼叫SessionManager:有部落格說Seata的中的事務也叫會話,會話管理器也叫事務管理器。我們就這樣叫吧

事務管理器

SessionManager

SessionManager是一個介面,我們來看它的繼承關係:
image*這種結構還挺常見的,Seata的Netty模組也是這樣,一個頂級介面,一個抽象類,然後下面就是具體模式的實現類。

SessionLifecycleListener

  • 首先它繼承了介面SessionLifecycleListener,這是一個會話生命週期的監聽器,(使用了觀察者模式),此介面定義了一系列要監聽的事件:
public interface SessionLifecycleListener {
    /**
     * 監聽全域性事務的開啟,當處理全域性事務開啟請求時,會呼叫該方法
     */
    void onBegin(GlobalSession globalSession) throws TransactionException;

    /**
     * 監聽全域性事務物件GlobalSession的狀態變化,只要是GlobalSession的狀態發生變化,就會呼叫該方法
     */
    void onStatusChange(GlobalSession globalSession, GlobalStatus status) throws TransactionException;

    /**
     * 監聽分支事務狀態的變化,在處理分支狀態報告請求時,會呼叫該方法
     */
    void onBranchStatusChange(GlobalSession globalSession, BranchSession branchSession, BranchStatus status)
        throws TransactionException;

    /**
     * 監聽新的分支事務註冊
     */
    void onAddBranch(GlobalSession globalSession, BranchSession branchSession) throws TransactionException;

    /**
     * 監聽分支事務從全域性事務物件中移除,
     * 當處理全域性事務回滾請求全域性事務提交請求時,都會有移除分支事務的動作,因此都會觸發該方法
     */
    void onRemoveBranch(GlobalSession globalSession, BranchSession branchSession) throws TransactionException;

    /**
     * 監聽全域性事務關閉,也就是監聽GlobalSession的close方法。
     * 在處理全域性事務提交請求和全域性事務回滾請求時,都會呼叫GlobalSession的close方法。
     */
    void onClose(GlobalSession globalSession) throws TransactionException;

    /**
     * 監聽全域性事務終止,也就是監聽GlobalSession的end方法。
     * 當要求全域性事務提交或者回滾時,無論最後成功與否,seata都會呼叫GlobalSession的end方法,因此都會觸發onEnd
     */
    void onEnd(GlobalSession globalSession) throws TransactionException;
}

SessionManager則定義了GlobalSession狀態發生變化時應該執行的動作方法

public interface SessionManager extends SessionLifecycleListener, Disposable {

    /**
     * 將全域性事務物件新增到會話管理器中,當全域性事務非同步提交或者非同步回滾時,都會呼叫該方法
     */
    void addGlobalSession(GlobalSession session) throws TransactionException;

    /**
     * 根據XID查詢GlobalSession
     */
    GlobalSession findGlobalSession(String xid) ;

    /**
     * 不同的儲存模式下,本方法和上面的方法實現不同,如果儲存模式是file,則兩個方法完全一致,
     * 如果儲存模式是db,則上面的方法相當於呼叫findGlobalSession(xid, true)
     * 如果第二個引數為true,表示返回的GlobalSession物件中帶有分支事務集合
     */
    GlobalSession findGlobalSession(String xid, boolean withBranchSessions);

    /**
     * 更新事務物件的狀態
     */
    void updateGlobalSessionStatus(GlobalSession session, GlobalStatus status) throws TransactionException;

    /**
     * 從管理器中移除GlobalSession
     * 當非同步提交重試超時時,會呼叫該方法
     */
    void removeGlobalSession(GlobalSession session) throws TransactionException;

    /**
     * 向GlobalSession中新增分支事務物件,當分支事務註冊時,會呼叫該方法
     */
    void addBranchSession(GlobalSession globalSession, BranchSession session) throws TransactionException;

    /**
     * 更新分支事務狀態
     */
    void updateBranchSessionStatus(BranchSession session, BranchStatus status) throws TransactionException;

    /**
     * 從全域性事務中移除分支事務,當全域性事務提交或者回滾時,會呼叫該方法
     */
    void removeBranchSession(GlobalSession globalSession, BranchSession session) throws TransactionException;

    /**
     * 返回所有的全域性會話物件
     */
    Collection<GlobalSession> allSessions();

    /**
     * 根據條件查詢符合要求的GlobalSession
     */
    List<GlobalSession> findGlobalSessions(SessionCondition condition);

    /**
     * 對全域性事務物件加鎖,當修改全域性事務物件的狀態時,都會加鎖
     */
    <T> T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable<T> lockCallable)
            throws TransactionException;
}

AbstractSessionManager

AbstractSessionManager是下一層的封裝,它有三個實現類,分別對應三種儲存模式:檔案,資料庫和Redis
它實現了SessionManager中定義的方法,還增加了一個重要的方法:writeSession,對Session管理的方法大多都直接或間接地呼叫了writeSession;我們簡單來看一個:

    @Override
    public void removeGlobalSession(GlobalSession session) throws TransactionException {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("MANAGER[{}] SESSION[{}] {}", name, session, LogOperation.GLOBAL_REMOVE);
        }
        writeSession(LogOperation.GLOBAL_REMOVE, session);
    }

AbstractSessionManager中還持有一個變數:事務儲存管理器,也有三種實現,分別是檔案,資料庫和Redis。下面就繼續分析它。

   /**
     * The Transaction store manager.
     */
    protected TransactionStoreManager transactionStoreManager;

事務儲存管理器

上面我們基本瞭解了Seata的事務管理器,它們的作用是對Seata的事務進行管理,管理好了就要儲存起來 。AbstractSessionManager這個類中還持有了TransactionStoreManager,它是真正用來實現儲存事務狀態的。
下面我們以RedisTransactionStoreManager為例進行分析:

image

RedisTransactionStoreManager

image
可以看到有不少insert,delete的操作,說明這裡就真正將事務資訊存到Redis的邏輯了,我們挑一個來看一下:

 /**
     * Insert the global transaction.
     * @param globalTransactionDO
     * @return
     */
	 // GlobalTransactionDO:要插入資料庫的類,和global_table的欄位是一一對應的
    private boolean insertGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) {
        // 獲取全域性事務的鍵
		String globalKey = buildGlobalKeyByTransactionId(globalTransactionDO.getTransactionId());
		//使用了Jedis和Pipeline
        try (Jedis jedis = JedisPooledFactory.getJedisInstance(); Pipeline pipelined = jedis.pipelined()) {
            Date now = new Date();
			//構建要插入的DO
            globalTransactionDO.setGmtCreate(now);
            globalTransactionDO.setGmtModified(now);
			//通過pipeline執行
            pipelined.hmset(globalKey, BeanUtils.objectToMap(globalTransactionDO));
            pipelined.rpush(buildGlobalStatus(globalTransactionDO.getStatus()), globalTransactionDO.getXid());
            pipelined.sync();
            return true;
        } catch (Exception ex) {
            throw new RedisException(ex);
        }
    }

TO BE CONTINUE...

相關文章