乐趣区

关于后端:gozero分布式事务实践

背景

随着业务的疾速倒退、业务复杂度越来越高,微服务作为最佳解决方案之一,它解耦服务,升高复杂度,减少可维护性的同时,也带来一部分新问题。

当咱们须要跨服务保证数据一致性时,原先的数据库事务力不从心,无奈将跨库、跨服务的多个操作放在一个事务中。这样的利用场景十分多,咱们能够列举出很多:

  • 跨行转账场景,数据不在一个数据库,但须要保障余额扣减和余额减少要么同时胜利,要么同时失败
  • 公布文章后,更新文章总数等统计信息。其中公布文章和更新统计信息通常在不同的微服务中
  • 微服务化之后的订单零碎
  • 出行游览须要在第三方零碎同时定几张票

面对这些本地事务无奈解决的场景,咱们须要分布式事务的解决方案,保障跨服务、跨数据库更新数据的一致性。

go-zero 与 dtm 强强联合,推出了在 go-zero 中无缝接入 dtm 的极简计划,让分布式事务的应用从未如此简略。

运行一个例子

咱们来看一个可运行的例子,而后再看如何本人开发实现一个残缺的分布式事务

上面以 etcd 作为注册服务中心,能够依照如下步骤运行一个 go-zero 的示例:

  • 配置 dtm

    MicroService:
      Driver: 'dtm-driver-gozero' # 配置 dtm 应用 go-zero 的微服务协定
      Target: 'etcd://localhost:2379/dtmservice' # 把 dtm 注册到 etcd 的这个地址
      EndPoint: 'localhost:36790' # dtm 的本地地址
  • 启动 etcd

    # 前提:已装置 etcd
    etcd
  • 启动 dtm

    # 前提:已配置好 dtm 的数据库链接
    go run app/main.go dev
  • 运行一个 go-zero 的服务

    git clone github.com/yedf/dtmdriver-clients && cd dtmdriver-clients
    cd gozero/trans && go run trans.go
  • 用 go-zero 发动一个 dtm 的事务

    # 在 dtmdriver-clients 的目录下
    cd gozero/app && go run main.go

当您在 trans 的日志中看到

2021/12/03 15:44:05 transfer out 30 cents from 1
2021/12/03 15:44:05 transfer in 30 cents to 2
2021/12/03 15:44:05 transfer out 30 cents from 1
2021/12/03 15:44:05 transfer out 30 cents from 1

那就是事务失常实现了

开发接入

参考 yedf/dtmdriver-clients 的代码

// 上面这行导入 gozero 的 dtm 驱动
import _ "github.com/yedf/dtmdriver-gozero"

// 应用 dtm 的客户端 dtmgrpc 之前,须要执行上面这行调用,告知 dtmgrpc 应用 gozero 的驱动来如何解决 gozero 的 url
err := dtmdriver.Use("dtm-driver-gozero")
// check err

// dtm 曾经通过后面的配置,注册到上面这个地址,因而在 dtmgrpc 中应用该地址
var dtmServer = "etcd://localhost:2379/dtmservice"

// 上面从配置文件中 Load 配置,而后通过 BuildTarget 取得业务服务的地址
var c zrpc.RpcClientConf
conf.MustLoad(*configFile, &c)
busiServer, err := c.BuildTarget()

  // 应用 dtmgrpc 生成一个音讯型分布式事务并提交
    gid := dtmgrpc.MustGenGid(dtmServer)
    msg := dtmgrpc.NewMsgGrpc(dtmServer, gid).
    // 事务的第一步为调用 trans.TransSvcClient.TransOut
    // 能够从 trans.pb.go 中找到上述办法对应的 Method 名称为 "/trans.TransSvc/TransOut"
    // dtm 须要从 dtm 服务器调用该办法,所以不走强类型,而是走动静的 url: busiServer+"/trans.TransSvc/TransOut"
        Add(busiServer+"/trans.TransSvc/TransOut", &busi.BusiReq{Amount: 30, UserId: 1}).
        Add(busiServer+"/trans.TransSvc/TransIn", &busi.BusiReq{Amount: 30, UserId: 2})
    err := msg.Submit()

整个开发接入的过程很少,后面的正文曾经很清晰,就不再赘述了

注意事项

在开发接入的过程中,去找 *.pb.go 的文件中的 grpc 拜访的办法门路时候,肯定要找 invoke 的门路

深刻了解动静调用

在 go-zero 应用 dtm 的分布式事务时,许多的调用是从 dtm 服务器发动的,例如 TCC 的 Confirm/Cancel,SAGA/MSG 的所有调用。

dtm 无需晓得组成分布式事务的相干业务 api 的强类型,它是动静的调用这些 api。

grpc 的调用,能够类比于 HTTP 的 POST,其中:

  • c.BuildTarget() 产生的 target 相似于 URL 中的 Host
  • “/trans.TransSvc/TransOut” 相当于 URL 中的 Path
  • &busi.BusiReq{Amount: 30, UserId: 1} 相当于 Post 中 Body
  • pb.Response 相当于 HTTP 申请的响应

通过上面这部分代码,dtm 就拿到了残缺信息,就可能发动残缺的调用了

Add(busiServer+"/trans.TransSvc/TransOut", &busi.BusiReq{Amount: 30, UserId: 1})

更加残缺的例子

热心的社区同学 Mikael 帮忙写了一个内容更加丰盛的例子,结合实际利用和子事务屏障,残缺的演示了一个线上理论运行的分布式事务,有趣味的同学能够参考:

https://github.com/Mikaelemmmm/gozerodtm

其余形式接入

go-zero 的微服务还有非 etcd 的其余形式,咱们顺次阐明他们的接入形式

直连

对于直连这种形式,您只须要在下面 dtm 的 etcd 配置根底上,将 Target 设置为空字符串即可。

直连的状况,不须要将 dtm 注册到注册核心

K8S

对于 K8S 这种形式,您只须要在下面 dtm 的 etcd 配置根底上,将 Target 设置为空字符串即可。

在 K8S 中,将服务注册到 K8S 中,是由 deployment.yaml 实现的,利用外部,不须要进行注册

直播分享预报

go-zero 的作者和我(dtm 的作者)将在 12 月 22 日晚 21 点,在 talkgo,联结做一场《go-zero 的分布式事务实际》的直播分享,将会带来更多更深刻的探讨。欢送大家届时加入。

直播地址为:https://live.bilibili.com/111…

小结

这一次 go-zero 与 dtm 的单干,在 go 生态中,打造了首个原生反对分布式事务的微服务解决方案,意义重大。

  • go-zero 我的项目地址:https://github.com/zeromicro/go-zero
  • dtm 我的项目地址:https://github.com/yedf/dtm

欢送大家应用咱们的 go-zerodtm,应用咱们原生的“分布式事务的微服务解决方案”,并 star 反对咱们

退出移动版