kratos分布式事务最佳实践

yedf · · 2131 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

# 背景 随着业务的快速发展、业务复杂度越来越高,微服务作为最佳解决方案之一,它解耦服务,降低复杂度,增加可维护性的同时,也带来一部分新问题。 当我们需要跨服务保证数据一致性时,原先的数据库事务力不从心,无法将跨库、跨服务的多个操作放在一个事务中。这样的应用场景非常多,我们可以列举出很多: - 跨行转账场景,数据不在一个数据库,但需要保证余额扣减和余额增加要么同时成功,要么同时失败 - 发布文章后,更新文章总数等统计信息。其中发布文章和更新统计信息通常在不同的微服务中 - 微服务化之后的订单系统 - 出行旅游需要在第三方系统同时定几张票 面对这些本地事务无法解决的场景,我们需要分布式事务的解决方案,保证跨服务、跨数据库更新数据的一致性。 [dtm](https://github.com/dtm-labs/dtm)作为一款非常流行的分布式事务框架,已经支持接入多种微服务框架,下面我们就来着重介绍一下 [https://github.com/go-kratos/kratos](https://github.com/go-kratos/kratos) 如何接入dtm,解决分布式事务的问题 ## 运行一个已有的示例 我们以 etcd 作为注册服务中心为例,按照如下步骤运行一个 kratos 的示例: - 启动 etcd ```shell # 请先确保已经安装 etcd etcd ``` - 配置 dtm ```yaml 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' ``` - 启动 dtm ```shell # 请先配置好dtm的数据库 go run app/main.go -c conf.yml # conf.yml 为你对应的 dtm 配置文件 ``` - 运行一个 kratos 的服务 ```shell 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](https://github.com/dtm-labs/dtmdriver-clients/blob/main/kratos/app/main.go) 的代码 ```go // 下面这些导入 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](https://github.com/dtm-labs/dtm),并 star 支持我们,一起共建 golang 的微服务生态 kratos地址:https://github.com/go-kratos/kratos 关注【分布式事务】公众号,获得更多分布式事务相关知识 dtm社区:添加dtm作者微信yedf2008进入社群 ![https://mmbiz.qpic.cn/mmbiz_jpg/9m9OfQX94VscRTgetU9ibThVcRxicFMxaq1j4COA8ibmQsfzDnEDxswYWmXZ5Vwuiaicy7sMTvjfoicKsr7lWIQHL73A/640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1](https://static.golangjob.cn/220331/f1a489330bc94185b02e7158561fb970.png)

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2131 次点击  ∙  1 赞  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传