简介: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 语言,在云原生和微服务相关技术方向均有涉猎,目前专攻分布式事务。
本文内容由阿里云实名注册用户自发贡献,版权归原做者全部,阿里云开发者社区不拥有其著做权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。若是您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将马上删除涉嫌侵权内容。