天天看點

Go Mysql Driver 內建 Seata-Golang 解決分布式事務問題

2020 年 4 月,我們開始嘗試實作 go 語言的分布式事務架構 Seata-Golang。衆所周知,Seata AT 模式以無業務代碼侵入的特點,被廣大開發者推崇。Java 版 Seata AT 模式通過對 DataSource 資料源進行代理,在 sql 語句執行時,對 sql 攔截解析,擷取資料庫對應資料在 sql 語句執行前後的副本,序列化後儲存起來,在 TC 協調復原時用來復原對應資料。實作 go 版本 client 的 AT 模式時,怎樣對業務開發者更友好,入侵更少,成了首要考慮的目标。

Go Mysql Driver 內建 Seata-Golang 解決分布式事務問題

作者 | 劉曉敏  GitHub ID:dk-lockdown

來源 | 阿裡巴巴雲原生公衆号

背景

Go Mysql Driver 內建 Seata-Golang 解決分布式事務問題

使用 go 操作資料庫時,我們會使用到 go 語言的官方庫

database/sql

,通過

sql.Open("mysql", ${dsn})

擷取一個資料源操作對象 db。開啟事務時,使用

db.Begin()

db.BeginTx(ctx, &sql.TxOptions{})

獲得事務操作對象 tx,執行 sql 查詢使用

tx.Query

;執行 sql 新增、修改、删除,使用

tx.Exec

;最後使用

tx.Commit()

送出或使用

tx.Rollback()

復原。

go 語言官方庫 

database/sql

提供了一個标準抽象層,通過實作不同的 driver 一套标準的抽象 API 可以操作不同的資料庫。開發 Go 版本的 AT 模式,必然要相容

database/sql

。通過研究

database/sql

的 api,建立資料源操作對象,資料庫有關的配置必須通過 Data Source Name (DSN) 抽象傳遞進去,下面是 DSN 的定義:

[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...&paramN=valueN]

實作 AT 模式對資料源代理是需要和事務協調器 TC 進行互動的,如果将 AT 模式實作在 driver 層,那麼和 TC 互動的一些參數必須要通過 DSN 傳遞到 driver,這樣有些破壞它的設計。是以,最後采取了一種折中方案,在

database/sql

層之上實作 AT 模式,代理

database/sql

建立出來的資料源操作對象。資料源代理對象實作

database/sql

庫定義的 Tx 接口,另外再提供一個開啟事務的方法:

Begin()

,雖然沒有完全相容

database/sql

的 api,但是關鍵接口和它的定義成一樣,勉強還能接受。到此,Seata-Golang 項目核心功能的開發已完成。

type Tx interface {
	Commit() error
	Rollback() error
}
           

轉折

Seata-Golang  開源後,逐漸被一些開發者了解和接觸,社群也對 Seata-Golang 發出了一些回報的聲音,不少開發者并不習慣寫原生 sql,他們希望将 Seata-Golang 內建到 ORM 架構,因為當時的設計沒有完全相容

database/sql

導緻內建上遇到一些困難。随着社群的熱切呼喚,且得益于前期對 driver 的一些研究,念念不忘必有回響,今年 3 月突然靈感迸發:為什麼參數一定要通過 DSN 傳遞?Seata-Golang Client 初始化後,在需要時通過 Client 端的 API

config.GetATConfig()

直接擷取使用不就可以了。

Go Mysql Driver 內建 Seata-Golang 解決分布式事務問題

于是工作之餘,曆時 2 周開發,第一個內建 Seata-Golang 的完全相容

database/sql

的 mysql driver 被開發出來,項目開源在 https://github.com/opentrx/mysql,現處于 beta 狀态,希望社群開發者使用後能有一些回報,可通過例子:https://github.com/opentrx/seata-go-samples,檢視使用方式并進行測試。

driver 的一些細節

  • 使用該 driver 進行分布式事務操作時,不能在 dsn 中設定 interpolateParams 參數為 true。

這涉及到 mysql 的兩個協定:Text 協定和 Binary 協定。有關兩個協定的差別,可以在文末參考文檔找到資料。實作該 driver 隻對 binary 協定進行了處理,開啟 interpolateParams 會使用 text 協定執行 sql。

  • 使用該 driver 在需要加入全局事務組和 tc 進行互動時,需要使用

    db.BeginTx(ctx context.Context, opts driver.TxOptions)

    方法,并在 ctx 中加入

    XID

    全局事務 id 的值。
ctx := context.WithValue(context.Background(), mysql.XID, c.Request.Header.Get("XID"))
tx, err := dao.BeginTx(ctx, &sql.TxOptions{
		Isolation: sql.LevelDefault,
		ReadOnly:  false,
	})
           

XID 傳遞到 driver 層,會儲存在 &mysqlConn 連接配接對象中,在和 TC 互動時用到。

  • 使用該 driver 的分布式事務功能前需要先初始化 seata-golang client 和 mysql driver:
config.InitConf(configPath)
  client.NewRpcClient()
  mysql.InitDataResourceManager()
  mysql.RegisterResource(config.GetATConfig().DSN)
           

具體可參考 seata-go-samples。

寄語

此項目開源到今年 4 月即滿一年,通過本文中的 mysql driver,希望能降低使用門檻,讓大家真正用起來,大家在選擇微服務開發技術棧時也不用擔心 go 語言沒有分布式事務處理方案。另外,此項目還很年輕,仍有許多需要完善的地方,希望感興趣的朋友一起參與到社群來對它進行完善!希望聽到社群更多使用者的回報!

如果你有任何疑問,歡迎釘釘掃碼加入交流群【釘釘群号 33069364】

作者簡介

劉曉敏 (GitHubID dk-lockdown),目前就職于 h3c 成都分公司,擅長使用 Java/Go 語言,在雲原生和微服務相關技術方向均有涉獵,目前專攻分布式事務。

參考資料

  • seata 官方
  • java 版 seata
  • seata-golang 項目位址
  • driver 位址
  • seata-golang go 夜讀 b站分享
  • 基于 getty 的 seata-golang 通信模型詳解
  • seata-golang 接入指南
  • mysql protocol