那些用 Go 實現的分佈式事務框架
圖片拍攝於 2021 年 12 月 04 日,山東青島。
開篇
不知不覺竟然一個月沒更新了,人一旦懶下來只會越來越懶。
最近對分佈式事務產生了一些興趣,查閱了一些文章以及論文。這篇文章主要介紹我看的兩個項目,不涉及一些理論知識。
-
阿里開源版本的 Seata,主要看了 Go 實現的 seata-golang(落後 java 版)
-
以及前段時間很多公衆號都發的 dtm。
Seata 簡介
Seata 是由阿里開源的分佈式事務服務,目前爲用戶提供了 AT、TCC、SAGA、XA 的事務模式,整體採用的是兩階段提交協議。Go 版的 seata-golang 目前好像只實現了 mysql 的 AT、TCC 模式,作者現在不咋更新了。
Seata 有幾個核心角色:
-
TC(Transaction Coordinator) - 事務協調者。(維護全局和分支事務的狀態,驅動全局事務提交或回滾)
-
TM(Transaction Manager)- 事務管理器。(定義全局事務的範圍:開始全局事務、提交或回滾全局事務。)
-
RM(Resource Manager)- 資源管理器。(管理分支事務處理的資源,與 TC 交談以註冊分支事務和報告分支事務的狀態,並驅動分支事務提交或回滾)
當然這樣看,可能還不是很理解,我拿一張官網的圖加以解釋。
從上圖中可以看出,這三個角色所負責的工作如下,
TC
-
維護全局和分支事務狀態,需要進行存儲。
-
當一個分佈式事務處理結束,需要通知到每個 RM 是 commit 還是 rollback。
TM
-
向 TC 請求開啓一個分佈式事務,得到一個全局唯一的分佈式 id。
-
根據每個參與分佈式事務的 RM 一階段的反饋,決定二階段向 TC 請求此次分佈式事務是 commit 還是 rollback(絕大部分場景下,一階段任一 RM 失敗,本次分佈式事務失敗)
RM
說的白一點就是管理參與分佈式事務的各個服務 (比如經典下單場景中涉及到的: 訂單服務、庫存服務、營銷服務等)
ps: 個人感覺,這裏的 RM 有點類似微服務中的中間處理層 (專業術語他們管這叫 bff->backend for fronted)。
-
一階段 prepare 行爲 (主動):每個 RM 調用 自定義 的 prepare 邏輯。
-
二階段 commit 行爲 (被動觸發):如果本次分佈式事務第一階段全部 RM 成功,TC 處理完自身狀態變更後,調用各個 RM 自定義 的 commit 邏輯。(一階段 RM 全部成功)
-
二階段 rollback 行爲 (被動觸發):如果本次分佈式事務第一階段任一 RM 失敗,TC 處理完自身狀態變更後,調用各個 RM 自定義 的 rollback 邏輯。(一階段任意 RM 失敗)
好了。下面可以看看 seata-golang 實現的一些細節了,seata-golang 底層採用 gRPC 進行通信。
seata-golang
我們先看 RM 部分結構。
至於 managers,保存支持的各大事務模式實現 (TCC、XA 等),每個模式只需要實現此接口即可。
再看 TC 部分結構 (去除部分字段)。
TC 對數據的存儲目前支持 mysql 和 pgsql,即只要實現 SessionManager 接口,然後注入到 SessionHolder 的 manager。
介紹完這兩個基本結構,還記得我們上面說過他們之間的關係嗎?
二階段 TC 會根據當前事務狀態去通知 RM 是 commit 還是 rollback。
在初始化 ResourceManager 的時候,
我們看到最終會調用 TC 一個 grpc 接口 branchCommunicate。
對應到服務端。
我們知道 gRPC 有四種基礎的通信模式。
-
一元模式(Unary RPC)
-
服務器端流 RPC(Server Sreaming RPC)
-
客戶端流 RPC(Client Streaming RPC)
-
雙向流 RPC(Bidirectional Streaming RPC)
想要流的形式也很簡單,只需要在 proto 方法定義中將對應的請求 | 響應 參數前加上 stream 標記,那麼這個接口就是流式傳送了。至於是哪種流,取決於你把 stream 加在哪邊,如果請求和響應都加,那麼就是雙向流了。
客戶端和服務端都可以通過 stream.Send 發送請求,通過 stream.Recv 接收數據。
當 RM 調用 BranchCommunicate 時,
最終處理分支事務調用 manager.BranchCommit,
相應的,當 TC 被 RM 調用 BranchCommunicate 後,
上面要發送給 RM 通知 commit 或者 rollback 數據是咋麼來的呢?
當 TC 要通知 RM 進行分支 commit 的時候,
最後一個就是 TM,沒啥理解難度。
其實 seat-golang 還有別的可以提一提的。
比如說,它裏面通過 go 反射實現的動態代理功能 (雖然我覺得完全沒必要?),我懶得寫了。
這篇文章再寫就更長了,不繼續寫 dtm 了,感興趣的留個言,我看看要不要寫一篇 dtm。
參考
-
https://seata.io/zh-cn/docs/overview/what-is-seata.html
-
https://github.com/opentrx/seata-golang
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/VnpFLfyrnNczhvK2p3gA_w