Seata-go TCC 設計與實現
本文主要介紹 seata-go 中 TCC 的設計思路、異常處理以及在實戰(zhàn)中的使用。
Seata 是一款開源的分布式事務解決方案,致力于為現代化微服務架構下的分布式事務提供高性能和簡單易用的分布式事務服務。Seata 將為用戶提供了 AT、TCC、SAGA 和 XA 等多種事務模式,幫助用戶解決不同場景下的業(yè)務問題。同時,Seata 還支持多語言編程,并且提供了簡易的 API 接口、豐富的文檔以及快速上手的 samples 示例項目,也能快速幫助開發(fā)者入門并上手 Seata 的使用。
Seata-go 是 Seata 多語言生態(tài)中 golang 語言的實現方案,它致力于幫助 golang 開發(fā)者也能使用 Seata 的能力來解決分布式事務場景的問題。Seata-go 復用了 Seata TC 的能力,client 的功能和 Seata 保持一致。目前 Seata-go 已經支持了 TCC 和 AT 模式,XA 模式正在測試中,預計會在 5 月份發(fā)版。Saga 模式正在設計和規(guī)劃中,后面也會和 Seata 的 Saga 功能保持一致。
本文主要從以下幾個角度,介紹 Seata-go 中的 TCC 模式的設計與使用:
Seata-go TCC 實現原理
Sata-go TCC 異常處理
Seata-go 的展望
Seata-go TCC 實現原理
Seata-go 采用了 getty 做 TCP 網絡通信,完全實現了 Seata 的通信協(xié)議。下層實現了配置中心和注冊中心,也支持了很多的第三方框架的接入,比如 dubbo、grpc、gorm 等等,目前也正在積極和各個社區(qū)溝通,以支持更多框架的接入。Seata-go 簡易的系統(tǒng)架構圖如下:
先來簡單回顧下 TCC 模式的含義。TCC 是分布式事務方案的一種實現,它采用了二階段提交協(xié)議,TCC 的全稱是 Try-Confirm-Cancel,Try 是預留資源操作,Confirm 是提交操作,Cancel 是回滾操作。在 TCC 的一階段中,先觸發(fā)所有的子事務執(zhí)行 Try 操作,如果所有的子事務的一階段都執(zhí)行成功,那么會觸發(fā)所有子事務二階段執(zhí)行 Confirm 操作,否則二階段執(zhí)行 Cancel 操作,以此來保證各個子事務狀態(tài)的一致性。
TCC 是一種侵入式的分布式事務方案,Try、Confirm 和 Cancel 三個階段的邏輯,都需要用戶自己去實現。這樣做意味著更多的代碼量,以及對業(yè)務很大的入侵性;而優(yōu)點是則比較靈活,能由用戶隨意發(fā)揮以解決更復雜的分布式事務場景的問題。
在介紹 Seata-go 的 TCC 模式之前,先來回顧下 Seata 中的三個核心角色,即 TC、TM 和 RM。TC 是事務協(xié)調者,負責維護全局事務的狀態(tài),以及觸發(fā)分支事務的提交和回滾動作;TM 是事務管理器,負責子事務的編排,以及全局事務的提交和回滾動作;RM 是資源管理器,管理分支事務處理的資源,比如 MySQL 數據庫的操作等。
了解了這三個核心角色,就可以大致的理解下 TCC 的事務流程,大致分為以下幾個步驟:
- TM 向 TC 發(fā)送請求,開啟全局事務,TC 側記錄下全局事務的狀態(tài)信息;
- TM 分別向所有的 RM 發(fā)送請求,RM 會向 TC 注冊分支事務,然后執(zhí)行 Try 階段的邏輯;
- 如果當中某個 RM 給 TM 返回 Try 階段執(zhí)行失敗,那 TM 就向 TC 發(fā)送“回滾全局事務” 的請求。TC 收到后,就會向所有已執(zhí)行 Try 的 RM 發(fā)送 Rollback 指令,觸發(fā) RM 執(zhí)行 Cancel 邏輯;
- 如果所有的 RM 都給 TM 返回 Try 階段執(zhí)行成功,那 TM 就向 TC 發(fā)送“提交全局事務” 的請求。TC 收到后,就會向所有已執(zhí)行 Try 的 RM 發(fā)送 Commit 指令,觸發(fā) RM 執(zhí)行 Commit 邏輯。
至此,一個完整的分布式事務就執(zhí)行完了,以下是這個過程的流程圖:
在 Seata-go 中,為了方便用戶使用,提供了兩種定義 TCC 服務方法,一種是實現 TwoPhaseInterface 接口,具體如下:
另一種是通過 tag 的方式來定義 TCC 服務,這種方式會相對復雜點,但是也更加的靈活:
第二種 tag 的方案,主要是為了滿足一些特殊的場景,比如說,dubbo-go 的 server 和 client 是使用 tag 的方式來定義的,這個時候就需要使用 tag 的方式來定義 TCC 的服務。一般情況推薦使用第一種繼承接口的方式來做,比較簡單。
在實際使用的時候,用戶只需要做以下幾件事情即可:
- 定義好自己的 TCC 服務,可以參考上面介紹的這兩種方式之一都可以;
- 調用 TCC 的代理方法 NewTCCServiceProxy ,將 TCC 服務的封裝成代理;
- 編排好自己的子事務,傳入到分布式事務的入口方法 WithGlobalTx 方法即可。
這里截圖給大家看個例子,更詳細的 samples 請參考 seata-go-samples 項目,地址為:
https://github.com/seata/seata-go-samples
Seata-go TCC 異常處理
在實際使用 TCC 的時候,由于網絡或是業(yè)務代碼邏輯執(zhí)行時間等因素,可能會出現以下的問題:
- 冪等:在事務的一、二階段,由于網絡延遲或是其他原因,RM 沒有及時給 TC 或 TM 響應,導致 RM 被重復觸發(fā)執(zhí)行一、二階段的邏輯,這個時候,需要考慮業(yè)務的冪等;
- 空回滾:由于網絡延遲或是其他原因,RM 在未收到 Try 請求的情況下,卻收到了 Rollback 請求,造成空回滾的問題;
- 懸掛:由于網絡延遲或是其他原因,RM 在未收到 Try 請求的情況下,收到了 Rollback 請求,處理完 Rollback 請求后,又收到了 Try 請求。這時全局事務已結束,會導致事務預留的資源一直無法釋放。
在 Seata-go 中,提供了兩種解決方案,來幫助用戶解決這個問題。
第一種方式的原理和 Seata Java 的處理邏輯是一樣的,都是借助 tcc_fence_log 事務狀態(tài)表來做的:
用戶需要在自己的業(yè)務數據庫中,創(chuàng)建這個表,RM 在提交業(yè)務 SQL 的時候,同時會在這個表里面插入一條記錄,這倆 SQL 是在一個本地事務中完成的。由于這個表中,“全局事務ID+分支事務ID”是一個聯(lián)合主鍵,導致重復執(zhí)行時會失敗,這樣就解決了 Try 階段的冪等問題。在 Commit 和 Cancel 階段時,會先查詢這個表中分支事務的狀態(tài),然后才進行實際的邏輯,最后再更新狀態(tài)。這樣也能保證 Commit 和 Cancel 階段的冪等性。
再來看看 Seata-go 是如何解決事務懸掛和空回滾的問題。假如一個 Rollbback 請求過來,RM 去查詢 tcc_fence_log 表,發(fā)現沒有記錄(因為 RM 尚未收到 Try 請求),此時會往 tcc_fence_log 表插入一條記錄,并標記狀態(tài)為 suspend,然后直接退出,而不會去執(zhí)行 Rollback 的邏輯,這樣就避免了空回滾的問題。如果 RM 后面再收到 Try 請求,由于 tcc_fence_log 表已經有一條記錄,就會導致事務 SQL 無法提交而失敗(tcc_fence_log 會出現主鍵沖突的問題),這樣就避免了防懸掛的問題。
要實現這種方式,需要使用 Seata-go 提供的代理數據源,這些操作都會由代理數據源來完成,用戶只需要開啟開關,關注自己的業(yè)務 SQL 即可,這個功能已經實現,會在后續(xù)進行發(fā)版。
第二種方式,是通過用戶手動的方式來實現的。原理和上面類似,但是 tcc_fence_log 的操作邏輯需要由用戶自己實現,下面的截圖描述了大致的使用方式,詳情可以參考這個 samples 代碼:
https://github.com/seata/seata-go-samples/tree/main/tcc/fence
Seata-go 展望
Seata-go 社區(qū)近期與不少國內 go 語言微服務框架以及 ORM 框架背后的開發(fā)社區(qū)達成合作,比如 GORM 框架,已經集成到了 Sample 中,后續(xù)會將更多的 ORM 框架集成在 Seata-go-Samples 項目中。與 MOSN 社區(qū)的合作也在推進中,可實現真正的基于 Seata 的 Transaction Mesh。
Seata-go 的 XA 模式會在5月份進行發(fā)版,屆時 Seata-go 將支持 TCC、XA 和 AT 三種事務模式。Seata-go 后續(xù)的中心將會在 Saga 模式功能的開發(fā)上。
當前的 Saga 模式僅實現了服務編排的正向推進與反向 Rollback 能力,更進一步的服務編排則可以實現 DAG、定時任務、任務批量調度,覆蓋工作流的所有流程,提升用戶在 Seata 這個平臺上的使用體驗。目前 Seata-go 依賴于 Seata Java 的 TC,按照這個工作計劃,可能需要在未來的 Seata-go 版本中實現一個功能更強大的 TC 調度。