在上一篇中通過閱讀Seata伺服器端的程式碼,我們瞭解到TC是如何處理來自使用者端的請求的,今天這一篇一起來了解一下使用者端是如何處理TC發過來的請求的。要想搞清楚這一點,還得從GlobalTransactionScanner說起。
啟動的時候,會呼叫GlobalTransactionScanner#initClient()方法,在initClient()中初始化TM和RM
TM初始化,主要是註冊各種處理器,最終構造一個處理器對映表,不再多說
HashMap<Integer/*MessageType*/, Pair<RemotingProcessor, ExecutorService>> processorTable = new HashMap<>(32);
重點關注RM初始化
RM初始化過程中,設定了 resourceManager 和 transactionMessageHandler,然後也是註冊各種處理器,最終也是構造一個訊息型別和對應的處理器的一個對映關係
可以看到,圖中上半部分是RM特有的,下半部分與TM初始化註冊處理器類似
然鵝,真正處理請求的還是靠呼叫各個處理器中的handler.onRequest()方法,於是問題的關鍵就很明顯了,就在於handler
1. ResourceManager
在瞭解ResourceManager之前,讓我們首先了解一下ResourceManagerInbound和ResourceManagerOutbound
ResourceManagerInbound是處理接收到TC的請求的,是TC向RM發請求
ResourceManagerOutbound是處理流出的訊息的,是RM向TC發請求
ResourceManager繼承了二者,所以既負責向TC發請求,又負責接收從TC來的請求。
還記得剛才在RMClient中是怎麼獲取ResourceManager的嗎?是呼叫DefaultResourceManager.get()獲取的
DefaultResourceManager.get()得到的是一個單例DefaultResourceManager,建立DefaultResourceManager的時候會構建一個分支型別與ResourceManager的一個Map
2. TransactionMessageHandler
TransactionMessageHandler負責處理接收到的RPC訊息
前面在 RMClient 中通過 DefaultRMHandler.get() 獲取 TransactionMessageHandler
3. 訊息處理
RMClient#init()的時候new了一個RmNettyRemotingClient
這裡要記住,rmNettyRemotingClient的兩個成員變數此時已經被賦值了:
RmNettyRemotingClient構造方法中呼叫父類別AbstractNettyRemotingClient的構造方法
可以看到,根據收到的RPC訊息型別,從processorTable中獲取對應的Processor,最後呼叫對應RemotingProcessor的process()方法進行處理訊息
RemotingProcessor的實現類很多,挑其中一個RmBranchCommitProcessor看一下
真相大白,最終還是調DefaultRMHandler#handle()
捋一下這個過程
最後,補充一個,this為什麼是DefaultRMHandler
補充二:AbstractTransactionRequestToRM
4. 分支事務提交(二階段)
交給AsyncWorker去執行
可以看到:
所以,RM收到TC發的提交指令後,僅僅只是刪除該事務的undo_log表記錄
5. 分支事務回滾(二階段)
與提交類似
所以,回滾就是根據事務的undo_log進行回滾
6. 總結
1、啟動時,自動代理資料來源,應用GlobalTransactionalInterceptor,初始化TM和RM
2、進入@GlobalTransactional業務方法時,TM向TC發請求申請開啟全域性事務,並獲得全域性事務ID
3、業務方法呼叫遠端服務介面完成業務處理
4、RM執行本地邏輯,註冊分支事務,獲取全域性鎖,成功後提交本地事務並寫入undo_log,本地事務提交成功後向TC報告分支事務
5、TM發起全域性事務提交請求,TC向所有已註冊的RM發請求,讓RM進行分支提交,刪除本地undo_log
6、若執行失敗,TM發起全域性事務回滾,TC向所有RM發請求,回滾分支事務,還原資料