Seata-Server 啓動流程源碼分析

認識Seata

Seata 是一款開源的分佈式事務解決方案,致力於提供高性能和簡單易用的分佈式事務服務。Seata 將爲用戶提供了 AT、TCC、SAGA 和 XA 事務模式,爲用戶打造一站式的分佈式解決方案。java

Seata的TXC模型

img

根據上圖可知整個TXC模型有三個重要的組件spring

  • TC 事務協調器,維護全局和分支事務的狀態,驅動全局事務提交或回滾。(單獨部署)
  • TM 事務管理器 定義全局事務的範圍:開始全局事務、提交或回滾全局事務。
  • RM 資源管理器 管理分支事務處理的資源,與TC交談以註冊分支事務和報告分支事務的狀態,並驅動分支事務提交或回滾。

簡單理解就是TM事務管理器經過RPC與TC通信請求開啓一個全局事務數組

簡單理解過程就是: Business做爲服務起始方(此時它是TM)發起全局事務並註冊到TC。在調用協同服務時,協同服務的事務分支事務會先完成階段一的事務提交或回滾,並生成事務回滾的undo_log日誌,同時註冊當前服務到TC並上報其事務狀態,歸併到同一個業務的全局事務中。此時若沒有問題繼續下一個服務的調用,期間任何服務的分支事務回滾,都會通知到TC,TC在通知全局事務包含的全部已完成一階段提交的分支事務回滾。若是全部分支事務都正常,最後回到全局事務發起方時,也會通知到TC,TC在通知全局事務包含的全部分支刪除回滾日誌。在這個過程當中爲了解決寫隔離和度隔離的問題會涉及到TC管理的全局鎖。服務器

那麼全局事務是如何在服務中傳遞的呢?實際在TM向TC請求開啓一個全局事務的時候,TC會響應一個全局事務XID,只須要TM在調用其餘協同服務時把XID傳遞給協同服務,這樣就能夠實現全局事務在分佈式服務中傳播,以及分支事務屬於哪一個全局事務。框架

Seata目前已經支持許多框架中的XID的自動傳遞了分佈式

  • dubbo
  • spring cloud
  • sofa-rpc

用戶在使用Seata的時候對於XID的傳遞徹底是無感知。函數

上文提到Seata中三個重要的組件TC TM RM.性能

其中TC做爲事務協調者, 它負責驅動全局事務的提交與回滾。根據它的職責可知。它的重要性不言而喻。ui

那麼做爲一個優秀的協調者它須要具有哪些功能呢?this

  • 高可用
  • 高性能
  • 支持擴展

那麼咱們根據咱們的猜想來看看TC的實現模塊Server是怎麼來實現這寫功能的。

Server模塊介紹

image-20200331221613306

整個Server模塊能夠分紅7個主要模塊

  • RPC模塊 負責與TM RM交互
  • Coordinator Core模塊 TC實現事務協調的核心模塊
  • Lock模塊 資源全局鎖的實現
  • Config模塊 支持配置TC的配置模塊
  • Store模塊 TC運行時全局事務以及分支事務的相關信息須要經過Store模塊持久化
  • Discover模塊 Seata TC服務註冊發現模塊
  • HA-Cluste模塊 TC Server實現高可用的模塊

就一個Server端而言, 它就有7個模塊。那麼咱們改從何看起呢。

咱們能夠用Server啓動的main函數來理解清楚整個TC的運行流程

Server啓動流程

本文全部源碼基於Seata1.1.0 我的能力有限,若有不對歡迎指出。

整個Server端是一個java應用,它是經過java -jar啓動的,因此主入口是一個main函數。

入口地址是io.seata.server.Server#main()

public static void main(String[] args) throws IOException {
        //一、 參數解析
        ParameterParser parameterParser = new ParameterParser(args);

        //二、 監控初始化
        MetricsManager.get().init();

        // 三、將存儲模式放到系統環境變量÷
        System.setProperty(ConfigurationKeys.STORE_MODE, parameterParser.getStoreMode());

        // 四、建立與RM TM通信的rpc服務器
        RpcServer rpcServer = new RpcServer(WORKING_THREADS);
        //server port
        rpcServer.setListenPort(parameterParser.getPort());
        UUIDGenerator.init(parameterParser.getServerNode());
        //log store mode : file, db
        // 五、設置資源存儲模式
        SessionHolder.init(parameterParser.getStoreMode());
        // 六、核心事務協調器建立
        DefaultCoordinator coordinator = new DefaultCoordinator(rpcServer);
        coordinator.init();
        // 七、把協調器做爲一個回調 傳給netty rpc模塊
        rpcServer.setHandler(coordinator);
        // 八、註冊JVM關閉構造函數 
        ShutdownHook.getInstance().addDisposable(coordinator);
        ShutdownHook.getInstance().addDisposable(rpcServer);

        //127.0.0.1 and 0.0.0.0 are not valid here.
        if (NetUtil.isValidIp(parameterParser.getHost(), false)) {
            XID.setIpAddress(parameterParser.getHost());
        } else {
            XID.setIpAddress(NetUtil.getLocalIp());
        }
        XID.setPort(rpcServer.getListenPort());

        try {
            // 九、啓動RPC模塊 監聽TM RM的請求
            rpcServer.init();
        } catch (Throwable e) {
            LOGGER.error("rpcServer init error:{}", e.getMessage(), e);
            System.exit(-1);
        }

        System.exit(0);
    }

首先看看參數解析,其實參數解析很簡單主要是經過JCommander解析main函數中的args數組,不過在須要注意的是,因爲Seata Server已經支持容器部署, 因此在容器環境啓動參數的建立跟正常啓動的參數是不一樣的。容器部署的啓動參數須要經過System.getenv獲取

io.seata.server.ParameterParser#init()

private void init(String[] args) {
        try {
            // 判斷啓動環境是不是容器
            boolean inContainer = this.isRunningInContainer();

            // 若是是容器啓動 則從系統環境變量讀取參數配置
            if (inContainer) {
                if (LOGGER.isInfoEnabled()) {
                    LOGGER.info("The server is running in container.");
                }
                this.seataEnv = StringUtils.trimToNull(System.getenv(ENV_SYSTEM_KEY));
                this.host = StringUtils.trimToNull(System.getenv(ENV_SEATA_IP_KEY));
                this.serverNode = NumberUtils.toInt(System.getenv(ENV_SERVER_NODE_KEY), SERVER_DEFAULT_NODE);
                this.port = NumberUtils.toInt(System.getenv(ENV_SEATA_PORT_KEY), SERVER_DEFAULT_PORT);
                this.storeMode = StringUtils.trimToNull(System.getenv(ENV_STORE_MODE_KEY));
            } else {
                // 不然使用JCommander 解析啓動參數
                JCommander jCommander = JCommander.newBuilder().addObject(this).build();
                jCommander.parse(args);
                if (help) {
                    jCommander.setProgramName(PROGRAM_NAME);
                    jCommander.usage();
                    System.exit(0);
                }
            }
            if (StringUtils.isNotBlank(seataEnv)) {
                System.setProperty(ENV_PROPERTY_KEY, seataEnv);
            }
            if (StringUtils.isBlank(storeMode)) {
                storeMode = ConfigurationFactory.getInstance().getConfig(ConfigurationKeys.STORE_MODE,
                    SERVER_DEFAULT_STORE_MODE);
            }
        } catch (ParameterException e) {
            printError(e);
        }

    }

拿到啓動參數後咱們就要根據啓動參數依次 啓動監控、設置存儲模型,建立協調核心對象、啓動Rpc服務器。

爲何Rpc服務器要在最後一個啓動呢? 下篇文章會解答。

因爲監控對Seata的核心功能暫無影響因此本文已經後續文章暫不對監控進行分析。

總結

本文簡單的介紹了一下Seata Server模塊啓動流程的一個分析,瞭解Seata的啓動流程,可是都是比較簡單沒有深刻,後續會陸續深刻分析Rpc模塊與核心協調模塊。

掃碼_搜索聯合傳播樣式-標準色版

相關文章
相關標籤/搜索