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


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