背景
隨著業務的快速發展、業務複雜度越來越高,微服務作為最佳解決方案之一,它解耦服務,降低複雜度,增加可維護性的同時,也帶來一部分新問題。
當我們需要跨服務保證資料一致性時,原先的資料庫事務力不從心,無法將跨庫、跨服務的多個操作放在一個事務中。這樣的應用場景非常多,我們可以列舉出很多:
- 跨行轉賬場景,資料不在一個資料庫,但需要保證餘額扣減和餘額增加要麼同時成功,要麼同時失敗
- 釋出文章後,更新文章總數等統計資訊。其中釋出文章和更新統計資訊通常在不同的微服務中
- 微服務化之後的訂單系統
- 出行旅遊需要在第三方系統同時定幾張票
面對這些本地事務無法解決的場景,我們需要分散式事務的解決方案,保證跨服務、跨資料庫更新資料的一致性。
dtm作為一款非常流行的分散式事務框架,已經支援了接入多種微服務框架,下面我們就來著重介紹一下go-kratos如何接入dtm,解決分散式事務的問題
執行一個例子
我們來看一個可執行的例子,然後再看如何自己開發完成一個完整的分散式事務
下面以etcd作為註冊服務中心,可以按照如下步驟執行一個kratos的示例:
配置dtm
MicroService: Driver: 'dtm-driver-kratos' # name of the driver to handle register/discover Target: 'discovery://127.0.0.1:2379/dtmservice' # register dtm server to this url EndPoint: 'grpc://localhost:36790'
啟動etcd
# 前提:已安裝etcd etcd
啟動dtm
# 請先配置好dtm的資料庫 go run app/main.go -c conf.yml # conf.yml 為你對應的 dtm 配置檔案
執行一個kratos的服務
git clone https://github.com/dtm-labs/dtmdriver-clients && cd dtmdriver-clients cd kratos/trans make build && ./bin/trans -conf configs/config.yaml
發起一個 kratos 使用 dtm 的事務
# 在 dtmdriver-clients 的目錄下 cd kratos/app && go run main.go
當您在trans的日誌中看到
INFO msg=config loaded: config.yaml format: yaml
INFO msg=[gRPC] server listening on: [::]:9000
2022/03/30 09:35:36 transfer out 30 cents from 1
2022/03/30 09:35:36 transfer in 30 cents to 2
那就是事務正常完成了
開發接入
參考 dtm-labs/dtmdriver-clients 的程式碼
// 下面這些匯入 kratos 的 dtm 驅動
import (
_ "github.com/dtm-labs/driver-kratos"
)
// dtm 已經經過前面的配置,註冊到下面這個地址,因此在 dtmgrpc 中使用該地址
var dtmServer = "discovery://localhost:2379/dtmservice"
// 業務地址,下面的 busi 換成實際在 server 初始化設定的名字
var busiServer = "discovery://localhost:2379/busi"
// 發起一個msg事務,保證TransOut和TransIn都會完成
gid := dtmgrpc.MustGenGid(dtmServer)
m := dtmgrpc.NewMsgGrpc(dtmServer, gid).
Add(busiServer+"/api.trans.v1.Trans/TransOut", &busi.BusiReq{Amount: 30, UserId: 1}).
Add(busiServer+"/api.trans.v1.Trans/TransIn", &busi.BusiReq{Amount: 30, UserId: 2})
m.WaitResult = true
err := m.Submit()
logger.FatalIfError(err)
深入理解動態呼叫
在 kratos 使用 dtm 的分散式事務時,許多的呼叫是從 dtm 伺服器發起的,例如 TCC 的Confirm/Cancel,SAGA/MSG 的所有呼叫。
dtm 無需知道組成分散式事務的相關業務 api 的強型別,它是動態的呼叫這些api。
grpc 的呼叫,可以類比於 HTTP 的 POST,其中:
- "/api.trans.v1.Trans/TransIn" 相當於 URL 中的 Path。請注意這個Path一定是要從TransIn的Invoke函式實現裡面找
- &busi.BusiReq{Amount: 30, UserId: 1} 相當於 Post 中 Body
- v1.Response 相當於HTTP請求的響應
通過下面這部分程式碼,dtm就拿到了完整資訊,就能夠發起完整的呼叫了
Add(busiServer+"/api.trans.v1.Trans/TransIn", &busi.BusiReq{Amount: 30, UserId: 1})
其他方式接入
kratos 的微服務還有非 etcd 的其他方式,下面列出它們的接入方式
直連
對於直連這種方式,您只需要在上面 dtm 的 etcd 配置基礎上,將 Target 設定為空字串即可。
直連的情況,不需要將 dtm 註冊到註冊中心
小結
歡迎使用 dtm,並 star 支援我們,一起共建 golang 的微服務生態