引言
本篇文章著重點(diǎn)在于調(diào)用流程分析,根據(jù)業(yè)務(wù)的發(fā)起到結(jié)束對(duì)Seata的TM掀泳、RM雪隧、TC模塊進(jìn)行源碼調(diào)用過程分析,選用Seata版本為0.7.1版本员舵,本篇文章分析均為Seata的AT事務(wù)脑沿,TM、RM模塊分析的比較單一马僻,只分析了邏輯調(diào)用庄拇,在分析TC模塊時(shí)候才具體的結(jié)合TM、RM模塊進(jìn)行邏輯調(diào)用的全過程交互分析
時(shí)序圖
筆者通過繪制時(shí)序圖韭邓,我們可以清晰的知道在集成Seata措近、ShardingSphere、Dubbo之后女淑,我們插入一條數(shù)據(jù)的整個(gè)內(nèi)部調(diào)用鏈邏輯
TM模塊分析
比較重要的類TransactionalTemplate瞭郑、DefaultGlobalTransaction、DefaultTransactionManager
查看TransactionalTemplate源碼可知
在全局事務(wù)攔截器GlobalTransactionalInterceptor中調(diào)用excute后鸭你,調(diào)用過程如上圖所示屈张,雖然注釋信息已經(jīng)解釋的很詳細(xì)了,首先獲取全局事務(wù)GlobalTransaction,默認(rèn)實(shí)現(xiàn)類為DefaultGlobalTransaction袱巨,然后開啟全局事務(wù)beginTransaction
TransactionManager是一個(gè)自定義SPI接口
默認(rèn)實(shí)現(xiàn)類為DefaultTransactionManager.繼續(xù)跟蹤begin過程
通過將消息發(fā)給TC阁谆,最終調(diào)用到AbstractRpcRemoting類下面的sendAsyncRequest消息發(fā)送接口,該方法中重要的地方,消息未發(fā)送就喚醒mergeLock同步的對(duì)象
然后就能找到AbstractRpcRemotingClient類下面的MergedSendRunnable消息發(fā)送線程愉老,因?yàn)樵摼€程是在初始化TMClient(TmRpcClient)和RMClient(RmRpcClient)的時(shí)候一并進(jìn)行初始化
知道了消息發(fā)送场绿,我們看看消息的返回結(jié)果是怎么得到的
消息的獲取運(yùn)用了Future模式,消息返回與賦值在哪俺夕?因?yàn)槲覀兿l(fā)送和接收肯定都是異步發(fā)送和接收裳凸,如果對(duì)Netty比較熟悉的同學(xué)可能會(huì)直接知道消息切入點(diǎn)贱鄙,消息接收位于AbstractRpcRemotingClient類channelRead接口劝贸,消息的返回和賦值都是在這里進(jìn)行,不熟悉Netty的同學(xué)也不必糾結(jié)逗宁,可以MessageFuture類查看哪里進(jìn)行setResultMessage的
根據(jù)消息ID從futures獲取MessageFuture并設(shè)置消息返回映九,因?yàn)槭荈uture模式,所以就拿到了消息的返回結(jié)果
我們已開啟全局事務(wù)為例分析了消息的收發(fā)瞎颗,在TC/Seata-server模塊中收發(fā)消息基本都一致件甥,所以不在進(jìn)一步分析TC模塊中的消息收發(fā)捌议。
我們著重分析TM模塊中的以下部分
我們通過時(shí)序圖可知,當(dāng)我們執(zhí)行業(yè)務(wù)時(shí)候引有,如果業(yè)務(wù)出現(xiàn)異常瓣颅,那么該異常會(huì)被捕獲,然后通知TC進(jìn)行全局回滾操作譬正,如果沒有異常宫补,那么就進(jìn)行全局二階段提交操作。但是可能會(huì)有同學(xué)比較好奇上圖中的rs = business.execute();這個(gè)是個(gè)回調(diào)接口曾我,回調(diào)實(shí)現(xiàn)類
這個(gè)簡單理解就是Spring AOP粉怕,會(huì)繼續(xù)調(diào)用增強(qiáng)鏈中的下一個(gè)(調(diào)用下一個(gè)攔截器),最后調(diào)用到我們的目標(biāo)執(zhí)行方法,如下圖所示
比如我們使用了Dubbo抒巢,那么當(dāng)我們執(zhí)行orderService.insertOrder(orderEntity);在這個(gè)方法體中贫贝,如果有異常,那么completeTransactionAfterThrowing會(huì)執(zhí)行蛉谜,這里簡單說明下稚晚,無論是當(dāng)前模塊的異常還是調(diào)用到下游出現(xiàn)異常,如使用Dubbo調(diào)用型诚,我們都可以理解成這些都是屬于同步調(diào)用蜈彼,上游模塊獲取到的異常信息可能會(huì)是rpc異常,也可能是反射異常俺驶,也可能是下游主動(dòng)拋出的異常幸逆,所以上游模塊只能獲取當(dāng)前模塊的完整異常信息,獲取到下游的異常不是完全正確的暮现。TM模塊分析完畢
RM模塊分析
比較重要的類DataSourceProxy还绘、ConnectionProxy、PreparedStatementProxy栖袋、ExecuteTemplate拍顷,大致調(diào)用關(guān)系為DataSourceProxy獲取連接ConnectionProxy,ConnectionProxy獲取預(yù)編譯PreparedStatementProxy塘幅,PreparedStatementProxy獲取SQL執(zhí)行器ExecuteTemplate昔案。我們已Insert舉例,查看ExecuteTemplate源碼
通過解析SQL得出不同的執(zhí)行器电媳,這里我們會(huì)執(zhí)行InsertExecutor踏揣,唯一可能需要注意的就是SelectForUpdateExecutor這個(gè)執(zhí)行器,簡單說下這個(gè)執(zhí)行器的業(yè)務(wù)場景匾乓,因?yàn)镽M是當(dāng)前模塊若沒有異常就會(huì)提交一階段數(shù)據(jù)入庫捞稿,但是,往往我們當(dāng)前模塊可能會(huì)有某些業(yè)務(wù)接口,這些業(yè)務(wù)接口需要的是二階段的最終數(shù)據(jù)娱局,所以這里我們就可以使用Seata的@GlobalLock全局鎖彰亥,這個(gè)會(huì)一直輪訓(xùn)直到獲取到二階段最終數(shù)據(jù).有興趣的同學(xué)可以仔細(xì)研究SelectForUpdateExecutor執(zhí)行器.返回來,我們還是繼續(xù)分析InsertExecutor衰齐,查看rs = executor.execute(args);源碼任斋,最終會(huì)調(diào)用到以下接口
判斷是否全局事務(wù),綁定XID耻涛,以及判斷是否有全局鎖注解仁卷,全局鎖注解使用上文已介紹
判斷是否自動(dòng)提交事務(wù),一般都是自動(dòng)犬第,若是自動(dòng)提交事務(wù)會(huì)進(jìn)行設(shè)置為false锦积,會(huì)調(diào)用到以下代碼
這個(gè)地方beforeImage主要是通過解析用戶執(zhí)行SQL,然后記錄執(zhí)行SQL前的快照歉嗓,然后執(zhí)行statementCallback.execute繼而調(diào)用到以下方法執(zhí)行SQL語句
afterImage同理類推丰介,記錄SQL執(zhí)行后的快照,prepareUndoLog準(zhǔn)備好undolog數(shù)據(jù),也就是回滾表undo_log中即將寫入的數(shù)據(jù)
connectionProxy.commit();調(diào)用到如下方法
首先判斷是否是全局事務(wù),若不是全局事務(wù)鉴分,也不是全局鎖哮幢,那么直接提交數(shù)據(jù),直接分析全局事務(wù)方法
首先注冊(cè)當(dāng)前分支事務(wù),然后判斷當(dāng)前上下文是否有undolog數(shù)據(jù),數(shù)據(jù)就是上文介紹的志珍,在執(zhí)行完SQL之后會(huì)執(zhí)行prepareUndoLog橙垢,內(nèi)部就會(huì)拼裝undo_log回滾表數(shù)據(jù)
最后執(zhí)行commit提交數(shù)據(jù),然后上報(bào)分支事務(wù)狀態(tài),至此整個(gè)RM分支本地事務(wù)一階段已完成伦糯,寫到這里柜某,可能還是會(huì)有同學(xué)有疑問,比如register注冊(cè)分支事務(wù)是如何和TC模塊交互的敛纲,TC模塊的調(diào)用流程又是什么喂击!這些我們?cè)诤笪闹vTC模塊一起分析下流程
TC模塊分析
比較重要的類AbstractTCInboundHandler、DefaultCoordinator淤翔、DefaultCore
我們看看Server類都做了什么事情.因?yàn)門C模塊基于Netty框架翰绊,所以在研究TC模塊的時(shí)候,若對(duì)Netty框架完全不了解的同學(xué)旁壮,可以先閱讀下Netty框架相關(guān)文檔监嗜,比如定義消息接收類,每個(gè)回調(diào)代表什么意思抡谐,發(fā)送消息給客戶端如何進(jìn)行裁奇。有了一定了解之后在閱讀TC模塊會(huì)更清晰。
言歸正傳童叠,通過Server類框喳,我們看到通過創(chuàng)建和啟動(dòng)RpcServer來進(jìn)行和TM和RM交互,繼續(xù)分析厦坛,設(shè)置RpcServer的handler-> DefaultCoordinator協(xié)調(diào)器.DefaultCoordinator繼承AbstractTCInboundHandler五垮。然后RpcServer繼承AbstractRpcRemotingServer,然后設(shè)置Netty框架所需eventLoopGroupWorker杜秸、eventLoopGroupBoss放仗,最后啟動(dòng)
所有消息入口為AbstractRpcRemoting類中channelRead方法,如果有同學(xué)說為什么是這個(gè)撬碟,Netty框架規(guī)則就是這樣诞挨!
下面我們通過RM模塊注冊(cè)一階段本地事務(wù)舉例,詳細(xì)分析整個(gè)RM和TC交互過程
通過查看源碼最終調(diào)用到以下方法
這個(gè)resourceManagers在哪里賦值的呢蛤?跟蹤發(fā)現(xiàn)是靜態(tài)初始化實(shí)例通過自定義SPI擴(kuò)展接口通過BranchType進(jìn)行賦值
我們上面獲取的是AT類型ResourceManager惶傻,所以實(shí)現(xiàn)類為DataSourceManager,最終調(diào)用到AbstractResourceManager類下branchRegister方法
消息發(fā)送sendMsgWithResponse在TM模塊已經(jīng)介紹過,忘了的同學(xué)可以閱讀上文中的分析其障。
我們著重講解消息發(fā)送到TC之后银室,TC模塊中的業(yè)務(wù)處理。
我們簡要查看下查看下TC模塊的UML類圖
我們通過類圖發(fā)現(xiàn),我們比較關(guān)心的就RpcServer、AbstractRpcRemotingServer二蓝、AbstractRpcRemoting這3個(gè)類奶陈,那我們查看下我們分支事務(wù)注冊(cè)是如何進(jìn)行的
RM模塊發(fā)送請(qǐng)求到TC最終執(zhí)行到以下方法
然后TC模塊接收消息入口為RpcServer類中以下方法
因?yàn)槲覀兪亲?cè)RM分支事務(wù)事件,所以直接會(huì)執(zhí)行super父類進(jìn)行消息解析和分發(fā)伦泥,查看父類如何處理的。
因?yàn)槲覀兊腞M分支事務(wù)注冊(cè)屬于請(qǐng)求事件,所以最終會(huì)執(zhí)行以下方法
messageExecutor為Server啟動(dòng)類中定義的線程池執(zhí)行器
最終調(diào)用到RpcServer中dispatch方法進(jìn)行消息的分發(fā)處理
查看RpcServer初始化接口可知
消息被派發(fā)到DefaultServerMessageListenerImpl類中進(jìn)行處理否过,繼續(xù)查看
我們的消息屬于MergedWarpMessage類型,繼續(xù)調(diào)用results[i] = transactionMessageHandler.onRequest(subMessage, rpcContext);這個(gè)transactionMessageHandler就是DefaultCoordinator(核心類之一惭蟋,事務(wù)協(xié)調(diào)器)叠纹。繼續(xù)分析
這個(gè)地方簡單解釋下,我們的請(qǐng)求BranchRegisterRequest繼承AbstractTransactionRequestToTC敞葛,然后就會(huì)調(diào)用到以下方法體
這個(gè)handler就是DefaultCoordinator本身實(shí)例誉察,因?yàn)镈efaultCoordinator繼承AbstractTCInboundHandler,所以會(huì)執(zhí)行到AbstractTCInboundHandler類中的以下方法
繼而調(diào)用到DefaultCoordinator類
最終調(diào)用到DefaultCore類方法
這個(gè)地方很關(guān)鍵,需要重點(diǎn)分析! 大致調(diào)用如下:
1:通過Xid獲取全局事務(wù)GlobalSession,GlobalSession全局事務(wù)加鎖調(diào)用邏輯
2:判斷GlobalSession狀態(tài)惹谐,判斷狀態(tài)是否可用或者有沒有發(fā)起全局事務(wù)(Begin狀態(tài))
3:創(chuàng)建BranchSession分支事務(wù)持偏,其中l(wèi)ockKeys簡單理解就是操作的哪些行數(shù)據(jù)字段
4:BranchSession分支事務(wù)加鎖,內(nèi)部加鎖大致為根據(jù)pk主鍵值+操作的行數(shù)據(jù)進(jìn)行加鎖(行鎖)氨肌,加鎖邏輯比較復(fù)雜鸿秆,可以在seata.io官網(wǎng)進(jìn)行查看原理圖,如果分支事務(wù)獲取鎖失敗了,那么代表有其他分支事務(wù)正在處理業(yè)務(wù)邏輯怎囚,拋出鎖沖突異常信息,該異常會(huì)被ConnectionProxy的以下代碼捕獲
然后拋出LockConflictException異常
最終AbstractDMLBaseExecutor.executeAutoCommitTrue捕獲這個(gè)LockConflictException鎖沖突異常.異常處理為:1->回滾數(shù)據(jù)卿叽。2->重試獲取分支事務(wù)鎖,默認(rèn)重試30次桥胞,每次sleep間隔時(shí)間10ms,直到成功執(zhí)行commit操作或者拋出鎖超時(shí),若拋出鎖超時(shí)則會(huì)一直上拋SQLException到TM發(fā)起全局事務(wù)的模塊,然后由TM模塊發(fā)起全局回滾消息到TC,由TC下發(fā)分支事務(wù)回滾消息
5:保存全局事務(wù)GlobalSession對(duì)應(yīng)的分支事務(wù)BranchSession數(shù)據(jù)到store考婴,根據(jù)SPI接口判斷采用file本地文件保存還是按照db數(shù)據(jù)庫進(jìn)行保存贩虾,以便于TC服務(wù)端自檢操作
6:返回BranchSession分支事務(wù)id
重點(diǎn):上文對(duì)分支事務(wù)BranchSession加鎖了,還沒有釋放鎖沥阱,下文將分析BranchSession分支事務(wù)鎖的釋放
我們?cè)谏衔闹械?步獲取到分支事務(wù)id后缎罢,我們繼續(xù)分析后續(xù)操作
1:注冊(cè)分支事務(wù)register()方法,獲取到分支事務(wù)id考杉,設(shè)置當(dāng)前ConnectionContext上下文的分支事務(wù)id
2:判斷當(dāng)前ConnectionContext上下文是否有保存的undo_log數(shù)據(jù)策精,若有數(shù)據(jù)則將數(shù)據(jù)寫入undo_log表
封裝BranchUndoLog分支事務(wù)數(shù)據(jù)
將數(shù)據(jù)插入undo_log表,狀態(tài)為Normal狀態(tài),該狀態(tài)有Normal崇棠、GlobalFinished 兩種狀態(tài),后文分支事務(wù)回滾時(shí)候著重講解這兩種狀態(tài)
3:提交當(dāng)前分支事務(wù)(本地事務(wù))
4:上報(bào)分支事務(wù)(本地事務(wù))一階段完成狀態(tài)PhaseOne_Done
5:清空上下文數(shù)據(jù)
假設(shè)各個(gè)分支事務(wù)均未出現(xiàn)異常咽袜,各個(gè)分支事務(wù)均已完成一階段并且上報(bào)PhaseOne_Done狀態(tài),那么發(fā)起全局事務(wù)TM模塊將執(zhí)行以下邏輯(TransactionalTemplate.commitTransaction)
然后調(diào)用到DefaultGlobalTransaction.commit,最終調(diào)用到DefaultTransactionManager.commit
和begin發(fā)起全局事務(wù)調(diào)用過程基本一致枕稀,不清楚流程的同學(xué)可以查看上文發(fā)起begin的調(diào)用過程酬蹋。調(diào)用順序:
1:DefaultTransactionManager.commit->發(fā)送commit消息
2:AbstractRpcRemoting.sendRequest->封裝/發(fā)送請(qǐng)求數(shù)據(jù)
3:RpcServer.channelRead->TC/seata-server獲取請(qǐng)求數(shù)據(jù)
4:AbstractRpcRemoting.channelRead->TC/seata-server獲取請(qǐng)求數(shù)據(jù)
5:RpcServer.dispatch->消息分發(fā)處理
6:DefaultServerMessageListenerImpl.onTrxMessage->消息監(jiān)聽實(shí)現(xiàn)類,如:對(duì)RpcMessage消息相關(guān)處理
7:DefaultCoordinator.onRequest設(shè)置消息Handler處理類
8:AbstractTCInboundHandler.handle消息映射處理,根據(jù)消息請(qǐng)求類型進(jìn)行映射處理抽莱,commit對(duì)應(yīng)的請(qǐng)求類型為GlobalCommitRequest
9:DefaultCoordinator.doGlobalCommit
10:DefaultCore.commit分析下
10-1:根據(jù)xid獲取全局事務(wù)范抓,注意這里是重點(diǎn),分支事務(wù)鎖釋放第一現(xiàn)場
此步驟會(huì)執(zhí)行g(shù)lobalSession.setActive(false);設(shè)置當(dāng)前全局事務(wù)為不可用狀態(tài)食铐,這樣就不會(huì)有分支事務(wù)繼續(xù)注冊(cè)進(jìn)globalSession匕垫。釋放當(dāng)前分支事務(wù)鎖,還記得上文講述過在分支事務(wù)提交commit時(shí)候需要先向TC注冊(cè)當(dāng)前分支事務(wù)嗎虐呻?這個(gè)注冊(cè)過程就涉及到分支事務(wù)鎖象泵,比如某個(gè)全局事務(wù)中的分支事務(wù)和另外一個(gè)全局事務(wù)中的分支事務(wù),都在操作某一行數(shù)據(jù)斟叼,那么就要等到這個(gè)分支事務(wù)鎖釋放后偶惠,其他分支事務(wù)才能進(jìn)行Commit操作,然后去獲取分支事務(wù)鎖.然后判斷當(dāng)前xid對(duì)應(yīng)的全局事務(wù)是否是發(fā)起狀態(tài)(begin)
10-2:判斷當(dāng)前全局事務(wù)是否可以異步執(zhí)行朗涩,判斷是否異步就是判斷是否是TCC或者是AT事務(wù)類型忽孽,是AT事務(wù)類型則可以異步執(zhí)行
將全局事務(wù)放入Map集合中
10-3:在啟動(dòng)TC/seata-server時(shí)初始化了以下方法
然后開啟各種類型的定時(shí)任務(wù)
異步提交全局事務(wù)就將在如下方法中執(zhí)行
10-4:遍歷全局事務(wù)sessionMap集合,循壞調(diào)用DefaultCore.doGlobalCommit,該過程相對(duì)復(fù)雜谢床,那就繼續(xù)分析分析
10-5:eventBus主要數(shù)據(jù)監(jiān)控統(tǒng)計(jì)作用
10-6:遍歷分支事務(wù),執(zhí)行resourceManagerInbound.branchCommit
10-7:調(diào)用DefaultCoordinator.branchCommit
10-8:RpcServer.sendSyncRequest發(fā)送消息給RM分支事務(wù)模塊
10-9:AbstractRpcRemotingClient.channelRead接收消息
10-10:AbstractRpcRemotingClient.dispatch消息分發(fā)處理
10-11:RmMessageListener.onMessage該消息監(jiān)聽兄一,該監(jiān)聽器在RMClient中進(jìn)行初始化設(shè)置
10-12:RmMessageListener.handleBranchCommit執(zhí)行以下方法
10-13:這個(gè)handler.onRequest,handler在初始化RMClient中進(jìn)行設(shè)置,所以該handler對(duì)象即DefaultRMHandler识腿,隨后調(diào)用過程為DefaultRMHandler父類AbstractRMHandler.onRequest->DefaultRMHandler.handle->RMHandlerAT父類AbstractRMHandler.handle->AbstractRMHandler.doBranchCommit最終調(diào)用如下方法
10-14:getResourceManager()為模版方法出革,實(shí)現(xiàn)類為RMHandlerAT類中,最終獲取到的ResourceManager實(shí)現(xiàn)類為DataSourceManager
10-15:DataSourceManager.branchCommit將分支事務(wù)加入asyncWorker定時(shí)任務(wù)中
asyncWorker在SPI接口加載DataSourceManager時(shí)初始化
10-16:最終調(diào)用asyncWorker.branchCommit
Offer操作加入隊(duì)列渡讼,然后直接返回分支狀態(tài)骂束,即PhaseTwo_Committed狀態(tài)
簡要分析下分支事務(wù)二階段提交做了哪些事情吧耳璧,便于更好理解流程
10-16-1:判斷隊(duì)列是否有任務(wù),若有任務(wù)展箱,則封裝mappedContexts集合數(shù)據(jù)
10-16-2:遍歷mappedContexts集合旨枯,獲取DataSourceProxy數(shù)據(jù)源代理對(duì)象,然后獲取Connection數(shù)據(jù)庫連接對(duì)象
10-16-3: 最終執(zhí)行UndoLogManager.batchDeleteUndoLog刪除undo_log表中xid和branch_id字段同時(shí)出現(xiàn)在xids集合和branchIds集合中的數(shù)據(jù)行,最終分支事務(wù)完成二階段提交
10-17:在10-16步驟中析藕,分支事務(wù)返回PhaseTwo_Committed狀態(tài)后,在10-12步驟中
RmMessageListener.handleBranchCommit方法中通過sender.sendResponse(request, serverAddress, resultMessage)將handler.onRequest結(jié)果發(fā)給TC/seata-server
10-18:TC/seata-server接收數(shù)據(jù)流程簡要概括,RpcServer.channelRead->消息賦值
10-19:最終我們10-6步驟就得到了BranchStatus分支事務(wù)狀態(tài),即PhaseTwo_Committed狀態(tài)
11:TC/seata-server獲取到PhaseTwo_Committed狀態(tài)之后或者其他狀態(tài)會(huì)按照不同策略進(jìn)行處理召廷,我們簡要分析PhaseTwo_Committed狀態(tài)
分析globalSession.removeBranch(branchSession)做了哪些事情
12:往sessionStore記錄當(dāng)前分支狀態(tài)凳厢,如file本地文件或者db數(shù)據(jù)庫账胧,釋放當(dāng)前分支事務(wù)鎖,最后分支事務(wù)集合中刪除當(dāng)前分支事務(wù),其實(shí)這個(gè)地方先紫,筆者也有一些疑慮治泥,就是上文中commit第一現(xiàn)場時(shí)候已經(jīng)釋放了分支事務(wù)鎖,為何這里還要進(jìn)行釋放遮精?筆者根據(jù)Rollback回滾初步判斷居夹,可能是為了處理Rollback回滾,因?yàn)榛貪L過程第一現(xiàn)場只是將globalSession設(shè)置為不可用狀態(tài)本冲,所以需要在removeBranch中進(jìn)行鎖釋放
至此准脂,整個(gè)Commit過程分析完畢,涵蓋分支事務(wù)一階段檬洞,分支事務(wù)二階段和TC服務(wù)端的一系列數(shù)據(jù)交互過程
上文分析了Commit過程狸膏,我們接著分析全局回滾Rollback過程,觸發(fā)全局回滾Rollback大致分為兩類:1:全局事務(wù)發(fā)起端內(nèi)部異常被捕獲添怔。2:發(fā)起端調(diào)用下游業(yè)務(wù)端湾戳,下游業(yè)務(wù)端主動(dòng)上拋各種異常信息被發(fā)起端捕獲.
最終在發(fā)起端TransactionalTemplate.completeTransactionAfterThrowing進(jìn)行異常捕獲
最終發(fā)起全局回滾請(qǐng)求
和上文講述的TransactionalTemplate.commitTransaction全局提交流程基本完全一致
大致調(diào)用過程為:
1:DefaultGlobalTransaction.rollback,最終調(diào)用到DefaultTransactionManager.rollback
2:AbstractRpcRemoting.sendRequest->封裝/發(fā)送請(qǐng)求數(shù)據(jù)
3:RpcServer.channelRead->TC/seata-server獲取請(qǐng)求數(shù)據(jù)
4:AbstractRpcRemoting.channelRead->TC/seata-server獲取請(qǐng)求數(shù)據(jù)
5:RpcServer.dispatch->消息分發(fā)處理
6:DefaultServerMessageListenerImpl.onTrxMessage->消息監(jiān)聽實(shí)現(xiàn)類,如:對(duì)RpcMessage消息相關(guān)處理
7:DefaultCoordinator.onRequest設(shè)置消息Handler處理類
8:AbstractTCInboundHandler.handle消息映射處理,根據(jù)消息請(qǐng)求類型進(jìn)行映射處理广料,rollback對(duì)應(yīng)的請(qǐng)求類型為GlobalRollbackRequest
9:DefaultCoordinator.doGlobalRollback
10:DefaultCore.rollback分析下
11:根據(jù)xid獲取全局事務(wù)砾脑,然后設(shè)置當(dāng)前globalSession不可用,然后判斷當(dāng)前xid對(duì)應(yīng)的全局事務(wù)是否是發(fā)起狀態(tài)(begin)
12:DefaultCore.doGlobalRollback
13:遍歷分支事務(wù),執(zhí)行resourceManagerInbound.branchRollback
14:調(diào)用DefaultCoordinator.branchRollback
15:RpcServer.sendSyncRequest發(fā)送消息給RM分支事務(wù)模塊
16:AbstractRpcRemotingClient.channelRead接收消息
17:AbstractRpcRemotingClient.dispatch消息分發(fā)處理
18:RmMessageListener.onMessage該消息監(jiān)聽艾杏,該監(jiān)聽器在RMClient中進(jìn)行初始化設(shè)置
19:RmMessageListener.handleBranchRollback執(zhí)行以下方法
20:這個(gè)handler.onRequest,handler在初始化RMClient中進(jìn)行設(shè)置韧衣,所以該handler對(duì)象即DefaultRMHandler,隨后調(diào)用過程為DefaultRMHandler父類
AbstractRMHandler.onRequest->DefaultRMHandler.handle->RMHandlerAT父類AbstractRMHandler.handle->AbstractRMHandler.doBranchRollback最終調(diào)用如下方法
21:getResourceManager()為模版方法购桑,實(shí)現(xiàn)類為RMHandlerAT類中汹族,最終獲取到的ResourceManager實(shí)現(xiàn)類為DataSourceManager
22:DataSourceManager.branchRollback
23:UndoLogManager.undo回滾步驟著重分析
24:查詢undo_log表是否存在branchId、xid對(duì)應(yīng)的數(shù)據(jù)
25:canUndo判斷當(dāng)前數(shù)據(jù)狀態(tài)是否是Normal正常狀態(tài)其兴,如不是Normal狀態(tài)則跳出while循壞顶瞒,Normal狀態(tài)是分支事務(wù)插入,是正常執(zhí)行流程元旬、GlobalFinished狀態(tài)為異常狀態(tài)榴徐,是全局事務(wù)發(fā)起的防御性插入,比如全局回滾時(shí)守问,分支事務(wù)還沒執(zhí)行,此時(shí)就需要插入防御性數(shù)據(jù),用主鍵沖突來防止異常分支事務(wù)的插入坑资,起了一個(gè)占位作用
26:解析undo_log數(shù)據(jù)行耗帕,獲取TableMeta表數(shù)據(jù),主要封裝數(shù)據(jù)為表的所有列名字段信息allColumns袱贮,表的所有索引信息allIndexes
27:根據(jù)執(zhí)行器進(jìn)行執(zhí)行仿便,如:Insert操作會(huì)使用afterImage數(shù)據(jù)進(jìn)行刪除當(dāng)前數(shù)據(jù)庫中的數(shù)據(jù)進(jìn)行數(shù)據(jù)回滾操作
28:如果undo_log表中存在branchId、xid對(duì)應(yīng)的數(shù)據(jù)則刪除分支事務(wù)undo_log表中branchId攒巍、xid對(duì)應(yīng)的數(shù)據(jù)嗽仪,如果undo_log中不存在branchId、xid對(duì)應(yīng)的數(shù)據(jù)則防御性插入一條branchId柒莉、xid數(shù)據(jù)
注釋信息已經(jīng)解釋的很清晰insertUndoLogWithGlobalFinished的作用闻坚,主要起防御性的作用,因?yàn)榉种聞?wù)和全局事務(wù)都是異步RPC調(diào)用兢孝,所以為了防止一些異常情況進(jìn)行的占位操作
至此對(duì)Seata AT模式整個(gè)TM,RM,TC調(diào)用流程分析完畢窿凤,若有不正確的地方歡迎指出!