Seata 是一款開源的分佈式事務解決方案,致力於提供高性能和簡單易用的分佈式事務服務。Seata 將爲用戶提供了 AT、TCC、SAGA 和 XA 事務模式,爲用戶打造一站式的分佈式解決方案。java
根據上圖可知整個TXC模型有三個重要的組件spring
簡單理解就是TM事務管理器經過RPC與TC通信請求開啓一個全局事務數組
簡單理解過程就是: Business做爲服務起始方(此時它是TM)發起全局事務並註冊到TC。在調用協同服務時,協同服務的事務分支事務會先完成階段一的事務提交或回滾,並生成事務回滾的undo_log日誌,同時註冊當前服務到TC並上報其事務狀態,歸併到同一個業務的全局事務中。此時若沒有問題繼續下一個服務的調用,期間任何服務的分支事務回滾,都會通知到TC,TC在通知全局事務包含的全部已完成一階段提交的分支事務回滾。若是全部分支事務都正常,最後回到全局事務發起方時,也會通知到TC,TC在通知全局事務包含的全部分支刪除回滾日誌。在這個過程當中爲了解決寫隔離和度隔離的問題會涉及到TC管理的全局鎖。服務器
那麼全局事務是如何在服務中傳遞的呢?實際在TM向TC請求開啓一個全局事務的時候,TC會響應一個全局事務XID,只須要TM在調用其餘協同服務時把XID傳遞給協同服務,這樣就能夠實現全局事務在分佈式服務中傳播,以及分支事務屬於哪一個全局事務。框架
Seata目前已經支持許多框架中的XID的自動傳遞了分佈式
用戶在使用Seata的時候對於XID的傳遞徹底是無感知。函數
上文提到Seata中三個重要的組件TC TM RM
.性能
其中TC做爲事務協調者, 它負責驅動全局事務的提交與回滾。根據它的職責可知。它的重要性不言而喻。ui
那麼做爲一個優秀的協調者它須要具有哪些功能呢?this
那麼咱們根據咱們的猜想來看看TC的實現模塊Server是怎麼來實現這寫功能的。
整個Server模塊能夠分紅7個主要模塊
就一個Server端而言, 它就有7個模塊。那麼咱們改從何看起呢。
咱們能夠用Server啓動的main函數來理解清楚整個TC的運行流程
本文全部源碼基於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模塊與核心協調模塊。