go-zero 分布式事務最佳實踐
隨著業(yè)務的快速發(fā)展、業(yè)務復雜度越來越高,微服務作為最佳解決方案之一,它解耦服務,降低復雜度,增加可維護性的同時,也帶來一部分新問題。
當我們需要跨服務保證數(shù)據(jù)一致性時,原先的數(shù)據(jù)庫事務力不從心,無法將跨庫、跨服務的多個操作放在一個事務中。這樣的應用場景非常多,我們可以列舉出很多:
跨行轉賬場景,數(shù)據(jù)不在一個數(shù)據(jù)庫,但需要保證余額扣減和余額增加要么同時成功,要么同時失敗
發(fā)布文章后,更新文章總數(shù)等統(tǒng)計信息。其中發(fā)布文章和更新統(tǒng)計信息通常在不同的微服務中
微服務化之后的訂單系統(tǒng)
出行旅游需要在第三方系統(tǒng)同時定幾張票
面對這些本地事務無法解決的場景,我們需要分布式事務的解決方案,保證跨服務、跨數(shù)據(jù)庫更新數(shù)據(jù)的一致性。
go-zero 與 dtm 強強聯(lián)合,推出了在 go-zero 中無縫接入 dtm 的極簡方案,讓分布式事務的使用從未如此簡單。
# 運行一個例子
我們來看一個可運行的例子,然后再看如何自己開發(fā)完成一個完整的分布式事務
下面以 etcd 作為注冊服務中心,可以按照如下步驟運行一個 go-zero 的示例:
配置 dtm
MicroService:
????Driver:?'dtm-driver-gozero'?#?配置dtm使用go-zero的微服務協(xié)議
????Target:?'etcd://localhost:2379/dtmservice'?#?把dtm注冊到etcd的這個地址
????EndPoint:?'localhost:36790'?#?dtm的本地地址
啟動 etcd
#?前提:已安裝etcd
etcd
啟動 dtm
#?前提:已配置好dtm的數(shù)據(jù)庫鏈接
go?run?app/main.go?dev
運行一個 go-zero 的服務
git?clone?github.com/yedf/dtmdriver-clients?&&?cd?dtmdriver-clients
cd?gozero/trans?&&?go?run?trans.go
用 go-zero 發(fā)起一個 dtm 的事務
#?在dtmdriver-clients的目錄下
cd?gozero/app?&&?go?run?main.go
當你在 trans 的日志中看到
2021/12/03?15:44:05?transfer?out?30?cents?from?1
2021/12/03?15:44:05?transfer?in?30?cents?to?2
2021/12/03?15:44:05?transfer?out?30?cents?from?1
2021/12/03?15:44:05?transfer?out?30?cents?from?1
那就是事務正常完成了
# 開發(fā)接入
參考 yedf/dtmdriver-clients 的代碼
//?下面這行導入gozero的dtm驅動
import?_?"github.com/yedf/dtmdriver-gozero"
//?使用dtm的客戶端dtmgrpc之前,需要執(zhí)行下面這行調用,告知dtmgrpc使用gozero的驅動來如何處理gozero的url
err?:=?dtmdriver.Use("dtm-driver-gozero")
//?check?err
//?dtm已經通過前面的配置,注冊到下面這個地址,因此在dtmgrpc中使用該地址
var?dtmServer?=?"etcd://localhost:2379/dtmservice"
//?下面從配置文件中Load配置,然后通過BuildTarget獲得業(yè)務服務的地址
var?c?zrpc.RpcClientConf
conf.MustLoad(*configFile,?&c)
busiServer,?err?:=?c.BuildTarget()
??//?使用dtmgrpc生成一個消息型分布式事務并提交
?gid?:=?dtmgrpc.MustGenGid(dtmServer)
?msg?:=?dtmgrpc.NewMsgGrpc(dtmServer,?gid).
????//?事務的第一步為調用trans.TransSvcClient.TransOut
????//?可以從trans.pb.go中找到上述方法對應的Method名稱為"/trans.TransSvc/TransOut"
????//?dtm需要從dtm服務器調用該方法,所以不走強類型,而是走動態(tài)的url:?busiServer+"/trans.TransSvc/TransOut"
??Add(busiServer+"/trans.TransSvc/TransOut",?&busi.BusiReq{Amount:?30,?UserId:?1}).
??Add(busiServer+"/trans.TransSvc/TransIn",?&busi.BusiReq{Amount:?30,?UserId:?2})
?err?:=?msg.Submit()
整個開發(fā)接入的過程很少,前面的注釋已經很清晰,就不再贅述了。
?注意事項
在開發(fā)接入的過程中,去找 *.pb.go 的文件中的 grpc 訪問的方法路徑時候,一定要找 invoke 的路徑


# 深入理解動態(tài)調用
在 go-zero 使用 dtm 的分布式事務時,許多的調用是從 dtm 服務器發(fā)起的,例如 TCC 的 Confirm/Cancel,SAGA/MSG 的所有調用。
dtm無需知道組成分布式事務的相關業(yè)務api的強類型,它是動態(tài)的調用這些api。
grpc的調用,可以類比于HTTP的POST,其中:
c.BuildTarget()產生的 target 類似于 URL 中的 Host/trans.TransSvc/TransOut相當于 URL 中的 Path&busi.BusiReq{Amount: 30, UserId: 1}相當于 Post 中 Bodypb.Response相當于 HTTP 請求的響應
通過下面這部分代碼,dtm 就拿到了完整信息,就能夠發(fā)起完整的調用了
Add(busiServer+"/trans.TransSvc/TransOut",?&busi.BusiReq{Amount:?30,?UserId:?1})
# 更加完整的例子
熱心的社區(qū)同學 Mikael 幫忙寫了一個內容更加豐富的例子,結合實際應用和子事務屏障,完整的演示了一個線上實際運行的分布式事務,有興趣的同學可以參考:
https://github.com/Mikaelemmmm/gozerodtm
# 其他方式接入
go-zero 的微服務還有非 etcd 的其他方式,我們依次說明他們的接入方式
?直連
對于直連這種方式,您只需要在上面 dtm 的 etcd 配置基礎上,將 Target 設置為空字符串即可。
直連的情況,不需要將 dtm 注冊到注冊中心。
?K8S
對于 K8S 這種方式,您只需要在上面 dtm 的 etcd 配置基礎上,將 Target 設置為空字符串即可。
在 K8S 中,將服務注冊到 K8S 中,是由 deployment.yaml 完成的,應用內部,不需要進行注冊。
# 直播分享預告
go-zero 作者和我(dtm 作者)將在12月22日晚21點,在 Go 夜讀,聯(lián)合做一場《go-zero 的分布式事務實踐》的直播分享,將會帶來更多更深入的討論。歡迎大家屆時參加。
直播地址為:https://live.bilibili.com/11171965
# 小結
這一次 go-zero 與 dtm 的合作,在 go 生態(tài)中,打造了首個原生支持分布式事務的微服務解決方案,意義重大。
go-zero項目地址:https://github.com/zeromicro/go-zero
dtm項目地址:https://github.com/yedf/dtm
歡迎大家使用 go-zero 和 dtm,使用我們原生的 分布式事務的微服務解決方案。
? ?

???
