kratos分散式事務實踐

葉東富發表於2022-03-30

背景

隨著業務的快速發展、業務複雜度越來越高,微服務作為最佳解決方案之一,它解耦服務,降低複雜度,增加可維護性的同時,也帶來一部分新問題。

當我們需要跨服務保證資料一致性時,原先的資料庫事務力不從心,無法將跨庫、跨服務的多個操作放在一個事務中。這樣的應用場景非常多,我們可以列舉出很多:

  • 跨行轉賬場景,資料不在一個資料庫,但需要保證餘額扣減和餘額增加要麼同時成功,要麼同時失敗
  • 釋出文章後,更新文章總數等統計資訊。其中釋出文章和更新統計資訊通常在不同的微服務中
  • 微服務化之後的訂單系統
  • 出行旅遊需要在第三方系統同時定幾張票

面對這些本地事務無法解決的場景,我們需要分散式事務的解決方案,保證跨服務、跨資料庫更新資料的一致性。

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 的微服務生態

相關文章