背景
隨著業務的快速發展、業務複雜度越來越高,微服務作為最佳解決方案之一,它解耦服務,降低複雜度,增加可維護性的同時,也帶來一部分新問題。
當我們需要跨服務保證資料一致性時,原先的資料庫事務力不從心,無法將跨庫、跨服務的多個操作放在一個事務中。這樣的應用場景非常多,我們可以列舉出很多:
- 跨行轉賬場景,資料不在一個資料庫,但需要保證餘額扣減和餘額增加要麼同時成功,要麼同時失敗
- 釋出文章後,更新文章總數等統計資訊。其中釋出文章和更新統計資訊通常在不同的微服務中
- 微服務化之後的訂單系統
- 出行旅遊需要在第三方系統同時定幾張票
面對這些本地事務無法解決的場景,我們需要分散式事務的解決方案,保證跨服務、跨資料庫更新資料的一致性。
go-zero與dtm強強聯合,推出了在go-zero中無縫接入dtm的極簡方案,讓分散式事務的使用從未如此簡單。
執行一個例子
我們來看一個可執行的例子,然後再看如何自己開發完成一個完整的分散式事務
下面以etcd作為註冊服務中心,可以按照如下步驟執行一個go-zero的示例:
配置dtm
MicroService: Driver: 'dtm-driver-gozero' # 配置dtm使用go-zero的微服務協議 Target: 'etcd://localhost:2379/dtmservice' # 把dtm註冊到etcd的這個地址 EndPoint: 'localhost:36790' # dtm的本地地址
啟動etcd
# 前提:已安裝etcd etcd
啟動dtm
# 前提:已配置好dtm的資料庫連結 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發起一個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
那就是事務正常完成了
開發接入
參考yedf/dtmdriver-clients的程式碼
// 下面這行匯入gozero的dtm驅動
import _ "github.com/yedf/dtmdriver-gozero"
// 使用dtm的客戶端dtmgrpc之前,需要執行下面這行呼叫,告知dtmgrpc使用gozero的驅動來如何處理gozero的url
err := dtmdriver.Use("dtm-driver-gozero")
// check err
// dtm已經通過前面的配置,註冊到下面這個地址,因此在dtmgrpc中使用該地址
var dtmServer = "etcd://localhost:2379/dtmservice"
// 下面從配置檔案中Load配置,然後通過BuildTarget獲得業務服務的地址
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伺服器呼叫該方法,所以不走強型別,而是走動態的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()
整個開發接入的過程很少,前面的註釋已經很清晰,就不再贅述了
注意事項
在開發接入的過程中,去找*.pb.go的檔案中的grpc訪問的方法路徑時候,一定要找invoke的路徑
深入理解動態呼叫
在go-zero使用dtm的分散式事務時,許多的呼叫是從dtm伺服器發起的,例如TCC的Confirm/Cancel,SAGA/MSG的所有呼叫。
dtm無需知道組成分散式事務的相關業務api的強型別,它是動態的呼叫這些api。
grpc的呼叫,可以類比於HTTP的POST,其中:
- c.BuildTarget() 產生的target類似於URL中的Host
- "/trans.TransSvc/TransOut" 相當於URL中的Path
- &busi.BusiReq{Amount: 30, UserId: 1} 相當於Post中Body
- pb.Response 相當於HTTP請求的響應
通過下面這部分程式碼,dtm就拿到了完整資訊,就能夠發起完整的呼叫了
Add(busiServer+"/trans.TransSvc/TransOut", &busi.BusiReq{Amount: 30, UserId: 1})
更加完整的例子
熱心的社群同學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點,在talkgo,聯合做一場《go-zero的分散式事務實踐》的直播分享,將會帶來更多更深入的討論。歡迎大家屆時參加。
直播地址為:https://live.bilibili.com/111...
小結
這一次go-zero與dtm的合作,在go生態中,打造了首個原生支援分散式事務的微服務解決方案,意義重大。
- go-zero專案地址:https://github.com/zeromicro/go-zero
- dtm專案地址:https://github.com/yedf/dtm
歡迎大家使用我們的go-zero
和dtm
,使用我們原生的“分散式事務的微服務解決方案”,並star支援我們