Seata-go TCC 設計與實現

本文主要介紹 seata-go 中 TCC 的設計思路、異常處理以及在實戰中的使用。

Seata 是一款開源的分佈式事務解決方案,致力於爲現代化微服務架構下的分佈式事務提供高性能和簡單易用的分佈式事務服務。Seata 將爲用戶提供了 AT、TCC、SAGA 和 XA 等多種事務模式,幫助用戶解決不同場景下的業務問題。同時,Seata 還支持多語言編程,並且提供了簡易的 API 接口、豐富的文檔以及快速上手的 samples 示例項目,也能快速幫助開發者入門並上手 Seata 的使用。

Seata-go 是 Seata 多語言生態中 golang 語言的實現方案,它致力於幫助 golang 開發者也能使用 Seata 的能力來解決分佈式事務場景的問題。 Seata-go 複用了 Seata TC 的能力,client 的功能和 Seata 保持一致。目前 Seata-go 已經支持了 TCC 和 AT 模式,XA 模式正在測試中,預計會在 5 月份發版。Saga 模式正在設計和規劃中,後面也會和 Seata 的 Saga 功能保持一致。

本文主要從以下幾個角度,介紹 Seata-go 中的 TCC 模式的設計與使用:

Seata-go TCC 實現原理

Seata-go 採用了 getty 做 TCP 網絡通信,完全實現了 Seata 的通信協議。下層實現了配置中心和註冊中心,也支持了很多的第三方框架的接入,比如 dubbo、grpc、gorm 等等,目前也正在積極和各個社區溝通,以支持更多框架的接入。Seata-go 簡易的系統架構圖如下:

先來簡單回顧下 TCC 模式的含義。TCC 是分佈式事務方案的一種實現,它採用了二階段提交協議,TCC 的全稱是 Try-Confirm-Cancel,Try 是預留資源操作,Confirm 是提交操作,Cancel 是回滾操作。在 TCC 的一階段中,先觸發所有的子事務執行 Try 操作,如果所有的子事務的一階段都執行成功,那麼會觸發所有子事務二階段執行 Confirm 操作,否則二階段執行 Cancel 操作,以此來保證各個子事務狀態的一致性。

TCC 是一種侵入式的分佈式事務方案,Try、Confirm 和 Cancel 三個階段的邏輯,都需要用戶自己去實現。這樣做意味着更多的代碼量,以及對業務很大的入侵性;而優點是則比較靈活,能由用戶隨意發揮以解決更復雜的分佈式事務場景的問題。

在介紹 Seata-go 的 TCC 模式之前,先來回顧下 Seata 中的三個核心角色,即 TC、TM 和 RM。TC 是事務協調者,負責維護全局事務的狀態,以及觸發分支事務的提交和回滾動作;TM 是事務管理器,負責子事務的編排,以及全局事務的提交和回滾動作;RM 是資源管理器,管理分支事務處理的資源,比如 MySQL 數據庫的操作等。

瞭解了這三個核心角色,就可以大致的理解下 TCC 的事務流程,大致分爲以下幾個步驟:

至此,一個完整的分佈式事務就執行完了,以下是這個過程的流程圖:

在 Seata-go 中,爲了方便用戶使用,提供了兩種定義 TCC 服務方法,一種是實現 TwoPhaseInterface 接口,具體如下:

另一種是通過 tag 的方式來定義 TCC 服務,這種方式會相對複雜點,但是也更加的靈活:

第二種 tag 的方案,主要是爲了滿足一些特殊的場景,比如說,dubbo-go 的 server 和 client 是使用 tag 的方式來定義的,這個時候就需要使用 tag 的方式來定義 TCC 的服務。一般情況推薦使用第一種繼承接口的方式來做,比較簡單。

在實際使用的時候,用戶只需要做以下幾件事情即可:

這裏截圖給大家看個例子,更詳細的 samples 請參考 seata-go-samples 項目,地址爲:

https://github.com/seata/seata-go-samples

Seata-go TCC 異常處理

在實際使用 TCC 的時候,由於網絡或是業務代碼邏輯執行時間等因素,可能會出現以下的問題:

在 Seata-go 中,提供了兩種解決方案,來幫助用戶解決這個問題。

第一種方式的原理和 Seata Java 的處理邏輯是一樣的,都是藉助 tcc_fence_log 事務狀態表來做的:

用戶需要在自己的業務數據庫中,創建這個表,RM 在提交業務 SQL 的時候,同時會在這個表裏面插入一條記錄,這倆 SQL 是在一個本地事務中完成的。由於這個表中,“全局事務 ID + 分支事務 ID” 是一個聯合主鍵,導致重複執行時會失敗,這樣就解決了 Try 階段的冪等問題。在 Commit 和 Cancel 階段時,會先查詢這個表中分支事務的狀態,然後才進行實際的邏輯,最後再更新狀態。這樣也能保證 Commit 和 Cancel 階段的冪等性。

再來看看 Seata-go 是如何解決事務懸掛和空回滾的問題。假如一個 Rollbback 請求過來,RM 去查詢 tcc_fence_log 表,發現沒有記錄(因爲 RM 尚未收到 Try 請求),此時會往 tcc_fence_log 表插入一條記錄,並標記狀態爲 suspend,然後直接退出,而不會去執行 Rollback 的邏輯,這樣就避免了空回滾的問題。如果 RM 後面再收到 Try 請求,由於 tcc_fence_log 表已經有一條記錄,就會導致事務 SQL 無法提交而失敗(tcc_fence_log 會出現主鍵衝突的問題),這樣就避免了防懸掛的問題。

要實現這種方式,需要使用 Seata-go 提供的代理數據源,這些操作都會由代理數據源來完成,用戶只需要開啓開關,關注自己的業務 SQL 即可,這個功能已經實現,會在後續進行發版。

第二種方式,是通過用戶手動的方式來實現的。原理和上面類似,但是 tcc_fence_log 的操作邏輯需要由用戶自己實現,下面的截圖描述了大致的使用方式,詳情可以參考這個 samples 代碼:

https://github.com/seata/seata-go-samples/tree/main/tcc/fence

Seata-go 展望

Seata-go 社區近期與不少國內 go 語言微服務框架以及 ORM 框架背後的開發社區達成合作,比如 GORM 框架,已經集成到了 Sample 中,後續會將更多的 ORM 框架集成在 Seata-go-Samples 項目中。與 MOSN 社區的合作也在推進中,可實現真正的基於 Seata 的 Transaction Mesh。

Seata-go 的 XA 模式會在 5 月份進行發版,屆時 Seata-go 將支持 TCC、XA 和 AT 三種事務模式。Seata-go 後續的中心將會在 Saga 模式功能的開發上。

當前的 Saga 模式僅實現了服務編排的正向推進與反向 Rollback 能力,更進一步的服務編排則可以實現 DAG、定時任務、任務批量調度,覆蓋工作流的所有流程,提升用戶在 Seata 這個平臺上的使用體驗。目前 Seata-go 依賴於 Seata Java 的 TC,按照這個工作計劃,可能需要在未來的 Seata-go 版本中實現一個功能更強大的 TC 調度。

Seata-go 社區目前正在快速生長中,希望有更多對開源感興趣的小夥伴,加入到我們社區來,一起助力 Seata-go 的成長!感興趣的小夥伴可以通過一下方式加入我們:

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ck921bCQzQp2Lg0BnKYp_Q