EasyTransaction是一個全功能的分散式事務框架,以下特性摘抄自其首頁:https://github.com/QNJR-GROUP/EasyTransaction
- 一個框架包含多種事務形態,一個框架搞定所有型別的事務
- 多種事務形態可混合使用
- 高效能,大多數業務系統瓶頸在業務資料庫,若不啟用框架的冪等功能,對業務資料庫的額外消耗僅為寫入25位元組的一行
- 可選的框架自帶冪等實現及呼叫錯亂次序處理,大幅減輕業務開發工作量,但啟用的同時會在業務資料庫增加一條冪等控制行
- 業務程式碼可實現完全無入侵
- 支援巢狀事務
- 無需額外部署協調者,不同APP的服務協調自身發起的事務,也避免了單點故障
- 分散式事務ID可關聯業務ID,業務型別,APPID,便於監控各個業務的分散式事務執行情況
本文主要分享EasyTransaction core中各個package的作用其主要實現。
請先閱讀 Seata架構的比對思考 https://www.cnblogs.com/skyesx/p/10674700.html ,再結合 程式碼以及demo除錯過程看這篇,直接看的話這裡的點太零碎了
一、context包
主要類
LogProcessContext
其用於儲存ET事務的上下文資訊。在開啟ET事務(第一次ET遠端呼叫,或者主動呼叫startSoftTrans方法)時,將建立本類的例項並將其與Spring的本地事務上下文繫結,通過:
TransactionSynchronizationManager.bindResource()
執行繫結。當需要取得ET上下文時,通過
TransactionSynchronizationManager.getResource()
取得。
ET上下文中包含的主要內容有:
- 最終事務狀態
- 全部的全域性事務日誌
- 未Flush到外部的全域性事務日誌
- 事務ID等內容
二、包core
本包主要類為
EasyTransFacade
TransactionHook
ConsistentGuardian
ExecuteCacheManager
類EasyTransFacade
其定義了業務呼叫方的介面,只包含兩個:
public void startEasyTrans(String busCode,long trxId);
public <P extends EasyTransRequest<R,E>,E extends EasyTransExecutor, R extends Serializable> Future<R> execute(P params);
第一個用於開啟全域性事務,主要的操作為:
- 掛載TransactionHook到當前的Spring本地事務中,使得可以在關鍵節點(如本地事務提交前、本地事務回滾後等等)嵌入ET的程式碼
- 將 LogProcessContext 繫結到當前的Spring本地事務,使得ET可以在當前Spring本地事務中隨時取得ET全域性事務的狀態。
- 在當前已開啟的本地事務中,寫入一條事務執行記錄到業務庫中,其對Crash恢復時識別全域性事務的狀態起關鍵作用
第二個表示執行某個遠端事務方法。
- 通過呼叫引數Object對應的Class獲取對應的處理器(如TCC處理器,可靠訊息處理器等)並執行呼叫,具體呼叫的形態後續專門的章節再繼續
基於註解的介面呼叫也是通過這兩個方法封裝而成。
類TransactionHook
其為ET框架程式碼與Spring原生事務的主要交界點,ET通過TransactionSynchronization定義的方法,在Spring本地事務執行過程中,擴充套件支援了全域性事務。主要擴充套件了以下兩個方法
beforeCommit(boolean readOnly)
afterCompletion(int status)
beforeCommit方法將會
- 在Spring本地事務提交前將所有未落盤的全域性事務日誌落盤
- 並執行所有未執行的遠端呼叫(ET會盡量延後全域性事務以此堆積並批量執行)
- 若有不成功的全域性事務,則丟擲異常,回滾事務(包括本地以及全域性)
afterCompletion方法將會
- 獲取本地事務的最終結果(提交/回滾/未知)以及 ET父級事務的狀態(提交/回滾/未知)來決定本級ET事務的最終狀態(提交/回滾/未知)
- 獲得最終的本級ET事務狀態後,非同步執行最終一致處理(呼叫consistentGuardian.process)
類ConsistentGuardian
本類用於處理ET全域性事務的最終一致,例如TCC的Conifrim/Cancel,可靠訊息的傳送訊息。
最終一致處理通常會在同步操作(TCC的TRY等)對應的本地事務執行完成後拋到執行緒池非同步執行,但執行失敗的話,會有兜底的補償(recovery包),後續再詳細講述
該類的主要工作機制是根據之前寫入的全域性事務日誌,獲取日誌對應的處理器(如從TCC的事務日誌獲取對應的TCC日誌處理器),以此
- 判斷當前ET事務的最終狀態(若當前ET事務狀態仍未確定的話)
- 傳入最終ET事務狀態到日誌處理器,依次處理對應的事務日誌,處理的典型過程例子:
- 若存在TRY方法對應的日誌
- 並且找不到TRY對應的CONFRIM/CANCEL日誌
- 則根據ET最終事務狀態,呼叫對應CONFIRM/CANCEL方法
類ExecuteCacheManager
本類主要服務於ET的以下期望
- 批量寫入ET事務日誌(以減少IO)
- 批量併發執行遠端業務呼叫(以減少序列等待遠端相應時間)
其主要實現的是,
- 對每個傳入的Calleble物件都返回一個經過改寫的Futrure物件
- 當任意一個Futrue的get方法都沒有被呼叫前,所有之前傳入的Callable物件都不會執行。
- 當任意一個Future的get被呼叫時,所有callable都會被批量執行,這裡包含了批量寫入日誌以及批量併發執行遠端呼叫
三、包datasource
主要包含以下兩個介面,其主要作用於業務資料來源。
DataSourceSelector
TransStatusLogger
類DataSourceSelector
該類主要用於獲取當前事務/請求對應的資料來源及其事務管理器,若應用有多個業務資料來源,則需要自行實現對應的資料來源選擇器,主要包含以下方法
DataSource selectDataSource(String appId,String busCode,long trxId);
DataSource selectDataSource(String appId,String busCode,EasyTransRequest<?, ?> request);
第一個方法是開啟ET事務時候選擇對應的資料來源
第二個方法是被呼叫方接受到請求時選擇對應的資料來源(用於冪等、防懸掛處理,若不需要可忽略)
該介面包含一個預設實現,當只有單資料來源時,可以直接用該實現
SingleDataSourceSelector
類TransStatusLogger
該類主要用來讀寫用於判斷ET事務狀態的記錄,該記錄會在ET事務開啟時,寫入當前的資料庫表中,隨著業務對應事務(Spring本地事務)提交而提交,回滾而回滾。
更具體請直接看實現
四、包executor
該包儲存的是事務發起方(遠端服務呼叫方)相關處理類的位置,不同的事務型別(TCC,可靠事務等)有不同的Executor,以TCC為例講解,其他的事務型別實現都類似。
TccMethodExecutor
該類實現了三個介面
- EasyTransExecutor
- LogProcessor
- DemiLogEventHandler
EasyTransExecutor介面定義了方法
<P extends EasyTransRequest<R,E>,E extends EasyTransExecutor,R extends Serializable> Future<R> execute(Integer sameBusinessCallSeq, P params);
該方法供類EasyTransFacade.execute使用,其對應的是執行TCC裡的TRY方法,具體的,它
- 將TRY方法呼叫對應的RPC請求包裝成Runnable類
- 構建本次呼叫對應的全域性事務日誌(主要包含本次呼叫的具體引數、對應遠端方法等)
- 然後傳入上面章節提到的類ExecuteCacheManager方法中
LogProcessor介面定義瞭如何處理事務日誌,其包含一個主要方法
boolean logProcess(LogProcessContext ctx, Content currentContent)
該方法將會判斷,如果傳入的日誌型別是PreTccCallContent(TCC TRY請求對應的日誌)的話,將會監聽該日誌最終的配對資訊(類ConsistentGuardian會在處理當前ET事務的日誌後,傳送訊息,告知所有需要配對的日誌的配對結果),如果
- 監聽到成功配對(找到CONFIRM或者CANCEL對應的日誌)的訊息,則不再做後續處理
- 監聽到配對失敗(沒有存在對應的CONFIRM/CANCEL日誌)的訊息,則根據當前的ET事務狀態執行對應的CONFIRM或者CANCEL操作,並記錄對應的日誌
其他的事務形態的實現也類似,不再贅述
五、包recovery
用於兜底恢復事務,實現最終一致。
程式碼不復雜,可以自行檢視。
六、包Filter
該包主要用於實現ET對應的Filter,該Filter作用於被呼叫端。我們可以通過實現ET的Filter擴充套件被呼叫端的功能,如處理冪等、處理巢狀事務、增加呼叫上下文的處理等等。
七、包idempotent
實現冪等、方懸掛等處理對應的包
冪等及防懸掛處理的主要原理:
- 當遠端呼叫過來時,寫入呼叫日誌到當前的開啟的業務日誌中,並記錄 呼叫對應的ID,呼叫引數對應的MD5
- 有結果返回時就將結果更新儲存到日誌中
- 當有重複請求過來時,就檢查ID對應的記錄是否存在,若存在則檢查引數的MD5是否一致,若一致則返回之前的儲存結果
- 防懸掛也類似,在上述的日誌中,將會記錄呼叫的方法是什麼,如
- 當找不到請求對應日誌時,但當前為cancel操作的話,框架將直接返回成功
- 上述cancel已經成功執行後,try方法再來到時,發現cancel已經執行,就直接將try報錯返回
八、包idgen
用於生成ET的分散式事務ID,當自行制定ID時,本包對應的方法不會被呼叫。當不指定ID時,將會自動生成一個。
九、包log
定義ET事務日誌對應的Class,以及其讀寫介面。
事務日誌在之前TccExecutor等章節已提到,不再贅述。
需要擴充套件事務日誌儲存實現的,直接實現以下介面即可
TransactionLogReader
TransactionLogWritter
(ET事務日誌的讀寫不需要與業務事務在同一個事務中,也不能在同一個事務中)
十、包master
用於在同一個appId中選擇一個作為master進行兜底最終一致補償的包。
實際上選擇不需要太精確,任意一個appId下的例項均可,也可以同時有多個master存在(但目前沒有意義,也會浪費效能)
十一、包monitor
用於提供ET例項狀態的包,可供Dashboard,監控等擴充套件使用
十二、包protocol
供客戶直接定義分散式事務服務的包,其包含一些客戶直接使用的 父類、配置介面、配置註解等
十三、包provider.factory
從Spring中獲取並儲存對應的bean實現,以便於快速方便地通過ET對應的定義,取得對應bean
十四、包queue
若要擴充套件新增對應的訊息佇列實現,則實現這個包對應的介面
十五、包rpc
同上
十六、包serialization
ET框架所使用的序列化形式,可自行擴充套件
十七、包stingcodec
用於壓縮字串,將字串替換為數字id,以提高儲存效率