簡介:2020 年 4 月,咱們開始嘗試實現 go 語言的分佈式事務框架 Seata-Golang。衆所周知,Seata AT 模式以無業務代碼侵入的特色,被廣大開發者推崇。Java 版 Seata AT 模式經過對 DataSource 數據源進行代理,在 sql 語句執行時,對 sql 攔截解析,獲取數據庫對應數據在 sql 語句執行先後的副本,序列化後保存起來,在 TC 協調回滾時用來回滾對應數據。實現 go 版本 client 的 AT 模式時,怎樣對業務開發者更友好,入侵更少,成了首要考慮的目標。
做者 | 劉曉敏 GitHub ID:dk-lockdown
來源 | 阿里巴巴雲原生公衆號html
2020 年 4 月,咱們開始嘗試實現 go 語言的分佈式事務框架 Seata-Golang。衆所周知,Seata AT 模式以無業務代碼侵入的特色,被廣大開發者推崇。Java 版 Seata AT 模式經過對 DataSource 數據源進行代理,在 sql 語句執行時,對 sql 攔截解析,獲取數據庫對應數據在 sql 語句執行先後的副本,序列化後保存起來,在 TC 協調回滾時用來回滾對應數據。實現 go 版本 client 的 AT 模式時,怎樣對業務開發者更友好,入侵更少,成了首要考慮的目標。java
使用 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()
回滾。mysql
go 語言官方庫 database/sql
提供了一個標準抽象層,經過實現不一樣的 driver 一套標準的抽象 API 能夠操做不一樣的數據庫。開發 Go 版本的 AT 模式,必然要兼容 database/sql
。經過研究 database/sql
的 api,建立數據源操做對象,數據庫有關的配置必須經過 Data Source Name (DSN) 抽象傳遞進去,下面是 DSN 的定義:git
[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
github
實現 AT 模式對數據源代理是須要和事務協調器 TC 進行交互的,若是將 AT 模式實如今 driver 層,那麼和 TC 交互的一些參數必需要經過 DSN 傳遞到 driver,這樣有些破壞它的設計。因此,最後採起了一種折中方案,在 database/sql
層之上實現 AT 模式,代理 database/sql
建立出來的數據源操做對象。數據源代理對象實現 database/sql
庫定義的 Tx 接口,另外再提供一個開啓事務的方法:Begin()
,雖然沒有徹底兼容 database/sql
的 api,可是關鍵接口和它的定義成同樣,勉強還能接受。到此,Seata-Golang 項目核心功能的開發已完成。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()
直接獲取使用不就能夠了。sql
因而工做之餘,歷時 2 周開發,第一個集成 Seata-Golang 的徹底兼容 database/sql
的 mysql driver 被開發出來,項目開源在 https://github.com/opentrx/mysql,現處於 beta 狀態,但願社區開發者使用後能有一些反饋,可經過例子:https://github.com/opentrx/seata-go-samples,查看使用方式並進行測試。數據庫
這涉及到 mysql 的兩個協議:Text 協議和 Binary 協議。有關兩個協議的區別,能夠在文末參考文檔找到資料。實現該 driver 只對 binary 協議進行了處理,開啓 interpolateParams 會使用 text 協議執行 sql。api
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 交互時用到。框架
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 語言,在雲原生和微服務相關技術方向均有涉獵,目前專攻分佈式事務。
本文內容由阿里雲實名註冊用戶自發貢獻,版權歸原做者全部,阿里雲開發者社區不擁有其著做權,亦不承擔相應法律責任。具體規則請查看《阿里雲開發者社區用戶服務協議》和《阿里雲開發者社區知識產權保護指引》。若是您發現本社區中有涉嫌抄襲的內容,填寫侵權投訴表單進行舉報,一經查實,本社區將馬上刪除涉嫌侵權內容。