那些用Go实现的分布式事务框架

03e8d18467a6e7c263b0f09784ec9ed6.png

开篇

最近对分布式事务产生了一些兴趣,查阅了一些文章以及论文。这篇文章主要介绍我看的两个项目,不涉及一些理论知识。

  • 阿里开源版本的Seata,主要看了Go实现的seata-golang(落后java版)

  • 以及前段时间很多公众号都发的dtm。

Seata简介

Seata是由阿里开源的分布式事务服务,目前为用户提供了AT、TCC、SAGA、XA的事务模式,整体采用的是两阶段提交协议。Go版的seata-golang 目前好像只实现了mysql的AT、TCC模式,作者现在不咋更新了。

Seata 有几个核心角色:

  • TC(Transaction Coordinator) -事务协调者。(维护全局和分支事务的状态,驱动全局事务提交或回滚)

  • TM(Transaction Manager)-事务管理器。(定义全局事务的范围:开始全局事务、提交或回滚全局事务。)

  • RM(Resource Manager)-资源管理器。(管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚)

当然这样看,可能还不是很理解,我拿一张官网的图加以解释。

816c933f42922fbbf022b9daea74ae8a.png

从上图中可以看出,这三个角色所负责的工作如下,

TC

  • 维护全局和分支事务状态,需要进行存储。

  • 当一个分布式事务处理结束,需要通知到每个RM是commit还是rollback。

TM

  • 向TC请求开启一个分布式事务,得到一个全局唯一的分布式id。

  • 根据每个参与分布式事务的RM一阶段的反馈,决定二阶段向TC请求此次分布式事务是commit还是rollback(绝大部分场景下,一阶段任一RM失败,本次分布式事务失败)

RM

说的白一点就是管理参与分布式事务的各个服务(比如经典下单场景中涉及到的:订单服务、库存服务、营销服务等)

ps:个人感觉,这里的RM有点类似微服务中的中间处理层(专业术语他们管这叫bff->backend for fronted)。

  • 一阶段 prepare 行为(主动):每个RM调用 自定义 的 prepare 逻辑。

  • 二阶段 commit 行为(被动触发):如果本次分布式事务第一阶段全部RM成功,TC处理完自身状态变更后,调用各个RM自定义 的 commit 逻辑。(一阶段RM全部成功)

  • 二阶段 rollback 行为(被动触发):如果本次分布式事务第一阶段任一RM失败,TC处理完自身状态变更后,调用各个RM自定义 的 rollback 逻辑。(一阶段任意RM失败)

好了。下面可以看看seata-golang 实现的一些细节了,seata-golang 底层采用gRPC进行通信。

seata-golang

我们先看RM部分结构。

541a6efdb6c0f2572ae06983ec2b652b.png

至于managers,保存支持的各大事务模式实现(TCC、XA等),每个模式只需要实现此接口即可。

82bb295628327d2f168649882ff80665.png

再看TC部分结构(去除部分字段)。

faa4d20f2f508e266e54435350a0ba30.png

TC对数据的存储目前支持mysql和pgsql,即只要实现SessionManager接口,然后注入到SessionHolder的manager。

介绍完这两个基本结构,还记得我们上面说过他们之间的关系吗?

二阶段TC会根据当前事务状态去通知RM是commit还是rollback。

在初始化ResourceManager 的时候,

99e15f325a879d88756c5e47fe5c2036.png

我们看到最终会调用TC一个 grpc 接口branchCommunicate。

5af45eef9744f1fe1ad8c316a5647202.png

对应到服务端。

f0864031944a4b97d0a66bd250c79951.png

我们知道gRPC有四种基础的通信模式。

  • 一元模式(Unary RPC)

  • 服务器端流RPC(Server Sreaming RPC)

  • 客户端流RPC(Client Streaming RPC)

  • 双向流RPC(Bidirectional Streaming RPC)

想要流的形式也很简单,只需要在proto方法定义中将对应的请求|响应 参数前加上stream标记,那么这个接口就是流式传送了。至于是哪种流,取决于你把stream加在哪边,如果请求和响应都加,那么就是双向流了。

客户端和服务端都可以通过stream.Send 发送请求,通过stream.Recv 接收数据。

当RM调用BranchCommunicate时,

f3d0c90926cf4d1c3832f996d69ce132.png

最终处理分支事务调用manager.BranchCommit,

956f25c47884a0ea8073f406b8cdf7dc.png

相应的,当TC被RM调用BranchCommunicate 后,

b8d499a9cae08d7aa6bdb5f0a814cdfd.png

上面要发送给RM 通知commit 或者 rollback 数据是咋么来的呢?

当TC要通知RM进行分支commit 的时候,

b5c0a857b01c5a0a97b561db1ca667ec.png

最后一个就是TM,没啥理解难度。

dcdf684fe877e5d55ff39200161ba537.png

其实seat-golang还有别的可以提一提的。

比如说,它里面通过go反射实现的动态代理功能(虽然我觉得完全没必要?),我懒得写了。

这篇文章再写就更长了,不继续写dtm了,感兴趣的留个言,我看看要不要写一篇dtm。

参考

  • https://seata.io/zh-cn/docs/overview/what-is-seata.html

  • https://github.com/opentrx/seata-golang

- END -

扫码关注公众号「网管叨bi叨」

给网管个星标,第一时间吸我的知识 👆

网管为大家整理了一本超实用的《Go 开发参考书》收集了70多条开发实践。去公众号回复【gocookbook】即刻领取!

觉得有用就点个在看  👇👇👇

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值