那些用Go實現的分布式事務框架之二
本文轉載自微信公眾號「RememberGo」,作者吳親庫里。轉載本文請聯系RememberGo公眾號。
開篇
上一篇那些用Go實現的分布式事務框架我們主要介紹的是seata-golang。一個對標seata的go語言實現,當然版本還是落后Java版很多的。
這次我們來介紹一下另一個go實現的分布式事務:dtm。
首先來看下dtm整體架構圖(來源官網)。
再來看之前的seata架構圖。
從架構上來看,大差不差。
seata中的TC對標dam的TM。
RM兩邊意思一致。
seata中的TM對標dtm事務SDK。作用都是一樣:第一階段開啟一個全局事務,執行各RM分支事務,第二階段根據RM第一階段執行結果,決定調用TC(seata)|TM(dtm) commit或者rollback。
架構上,個人感覺只是因為模塊名稱以及圖畫不一樣的差別,當然在實現細節上還是有很大差別的。
我們先簡單介紹下DTM各個模塊。
TM
TM 層在代碼中是沒有具體的主體結構的,開始都是函數之前的調用。
啟動TM實際上開啟了兩個服務,http以及grpc這兩個服務。
http路由,
gRPC接口,
即然提供了兩個服務入口,那理所當然有公共處理核心業務的部分。
TM對數據的存儲管理并不是依賴于接口,而是依賴于common.DB 結構。根據配置文件中DB.driver 的值決定底層數據庫是mysql還是postgres兩種。
再看這個DB結構,所以本質上無論底層是哪種數據庫,都是直接依賴gorm來對數據進行操作的。
接著,看下TM是如何通知各個RM進行commit或者rollback的?
舉一個TCC模式的例子。
TCC的兩個階段。
- 階段一: try。嘗試執行,調用各RM自定義的try行為,預留必要的業務資源。
- 階段二:Confirm(階段一所有參與本次事務的try行為都成功)。調用各分支事務的Confirm方法,真正執行業務,并且只使用try階段預留的資源。
- 階段二:Cancel(階段一任一參與本次事務的try行為失敗)。調用各分支事務的Cancel方法,釋放一階段try所預留的資源。
從上面我們可以得知,TCC模式下,TM在第二階段要么通知各分支事務Confirm要么Cancel。
在注冊各RM事務分支到TM的時候,最終TM會為每一個分布式事務的參與者(RM)生成兩條分支信息。
就像這樣,
對,就是把對應的RM資源操作地址直接存入。
當TM接收到commit或者rollback命令,在處理完自身邏輯(一般就是修改Gloable狀態),就需要開始處理每一個注冊進來的分支事務了,說白了就是需要調用各個分支事務對應操作的接口。
這里的t.getProcessor() 是需要根據當前事務的類型(TCC、SAGA、XA)獲取到對應的處理器來進行邏輯的處理。
當然,每個事務處理器只需要實現接口,
真正調用RM資源服務地址的時候,分為http和grpc,這是由開發者決定的。
在v1.6之前的版本,grpc的請求是很簡單粗暴解析地址方法然后連接的。
現在為了支持那些采用gRPC Resolver 機制之上的一些微服務框架接入,做了一塊抽象。感興趣[1]可以看下,這里就不介紹了。
SDK
至于SDK,每一個事務模式都是獨立的,本質上是沒有關聯的。比如下面我們啟動一個TCC分布式事務。這個分布式事務是由兩個服務組成,簡稱+30和-30的服務。
- 從上面的調用中我們還是能還原出整體流程。
- 調用TM,得到一個分布式id
- 調用TccGlobalTransaction函數開啟分布式事務。
- 調用TM prepare(這步只是為了查看第一步產生的那個分布式事務狀態是否處于prepare。這里沒看明白,此時還未注冊執行分支,全局狀態不是應該只會存在初始化狀態嗎)
- 上一步沒問題,執行傳入的閉包函數,即CallBranch 函數里向TM注冊參與事務的TM分支。注冊完成后,開始第一階段調用各分支的try服務。
- 各分支try服務調用結束,根據第一階段結果決定通知TM是submit還是abort。
另外提一點,分布式事務常見的一些問題:比如空補償、重掛等問題。
一般情況下,業務需要自行去處理這種場景,以免造成不可描述的錯誤。
dtm里面提供了對應子事務屏障方案。核心就在,
其實就是利用數據庫的唯一索引機制,當然每個RM資源你都得新增一張表。
上面提到,dtm的TM角色本質上就是對應 seata 中的 TC,但是他們的處理模式是不同的。
dtm中的TM會根據注冊時的各分支保存的地址,決定通過http還是rpc調用各RM操作,是由TM直接發起對RM的請求。
seata-go的實現中,TC是不參與直接調用RM的。
還記得上篇提到一個雙向流RPC接口(BranchCommunicate)。TC通過這個接口把對應分支處理信息傳遞給RM管理器。
然后由RM管理器根據事務類型選擇對應的事務管理器進行處理,最終調用的是對應事務類型管理器的BranchCommit方法。
下面是一個TCC事務類型管理器的處理。
對應的事務RM管理器是如何通知、處理各個RM資源的。
原理就是我上篇提到的作者實現的一個全局事務代理模式,本質上是利用go的反射實現的,感興趣的可以自己去扒下源碼,也可以看看作者對實現全局事務代理的介紹[2]。
總結
這篇文章主要介紹了dtm實現的一些細節,從這兩篇文章大體能看出實現上的部分區別,更多的細節還得靠自己去挖掘。
最后再問幾個問題,
- 日常開發中你們哪些場景是用到了分布式事務?用的是哪個框架還是自研的?
- 或者說在分布式環境下,一致性的問題你們是如何解決的?
相關
https://zhuanlan.zhihu.com/p/351391359
https://dtm.pub/protocol/support.html













































