写点什么

详解阿里开源分布式事务框架 Seata

  • 2021 年 11 月 25 日
  • 本文字数:4408 字

    阅读完需:约 14 分钟

Seata 发展历史

笔者于 2014 年开始着手解决阿里巴巴集团内部业务的分布式事务问题,从 0 到 1 研发一个支持非侵入模式(内部称之为 AT 模式,即自动模式)和 TCC 模式(内部称之为 MT 模式,即手动模式)的分布式事务中间件 TXC(Taobao Transaction Constructor)。TXC 被广泛应用于阿里巴巴集团内部业务,主要用于解决 HSF 服务框架下多个数据库读写间的一致性问题。在实际业务使用中,以非侵入模式为主,TCC 模式为辅助。

2016 年开始,这个产品以云服务的形式对外输出,名称为 GTS(Global Transaction Service),服务于众多大型私有云用户和公有云用户。

2019 年 1 月,阿里巴巴中间件团队推出了 GTS 开源版本 Fescar(Fast & Easy Commit And Rollback),并和开源社区一起共建开源分布式事务解决方案。Fescar 的愿景是:让分布式事务的使用像本地事务的使用一样简单和高效,并逐步解决开发者们遇到的分布式事务方面的所有难题。

在 Fescar 开源后,蚂蚁金服加入 Fescar 社区参与共建,随后 Fescar 被改名为 Seata(Simple Extensible Autonomous Transaction Architecture)。虽然 Seata 目前已经包含了多种事务模式,但其最吸引客户的始终是 AT 模式,因为技术发展趋势一定是从侵入式到非侵入式,提高研发效率。


Seata 已经是 Github 上一个大热的项目,开源两年多截止 2021 年 8 月已有两万多的“star”数和六千多的“fork”数,项目还在快速迭代中。目前 Seata 已成为业界主流的分布式事务解决方案。



Seata 总体架构

Seata 代码总量不大,目录结构比较简洁。

模块组成

Seata 的目录结构如下图所示。



各模块的说明如下。

  • all:只有一个 pom 文件,指定了 Seata 依赖哪些包。

  • bom:只有一个 pom 文件,指定了 Seata 依赖管理的包,即 pom 文件的<dependencyManagement>节点的内容。

  • changes:描述了版本变更情况。

  • common:通用模块。定义了通用的工具类、线程工具、异常、加载类等。

  • compressor:压缩模块。定义了多种主流压缩格式,比如 Zip、Gzip、7z、Lz4、Bzip2 等,用来实现消息压缩功能。

  • config:配置模块。用于连接和操作配置中心,支持多种主流配置中心组件,包括 Nacos、Apollo、Etcd、Consul、ZooKeeper 等。

  • core:核心模块。定义了 RPC、Netty、事件、协议、事务上下文等。

  • discovery:发现模块。用于服务发现,支持多种主流的可用作微服务注册中心的组件,包括 Nacos、Etcd、Eureka、Redis、ZooKeeper 等。

  • distribution:只有一个 pom 文件,用于打包发布。

  • integration:整合模块。整合了 Seata 对多种 RPC 框架的支持,包括 Dubbo、gRPC、SOFA-RPC 等。用来实现 Seata 事务上下文在 RPC 框架的传递。

  • metrics:度量模块。用于收集一些 Seata 运行指标数据,并导出到一些监控系统(如使用广泛的普罗米修斯)中。

  • rm:资源管理器模块。定义了多种类型资源管理器(AT 模式的资源管理器、TCC 模式的资源管理器、Saga 模式的资源管理器、XA 模式的资源管理器)的公共组件。

  • rm-datasource:AT 模式的资源管理器模块。实现了 AT 模式的数据源代理、SQL 语句处理等。该模块也包括对 XA 模式的支持。

  • saga:Saga 模式的资源管理器模块。用来实现对 Saga 模式事务的支持。

  • script:脚本模块。定义了需要的脚本文件和配置文件。

  • seata-spring-boot-starter:用于与 Spring Boot 结合,简化使用。

  • serializer:序列化模块。用于 Seata 消息序列化和反序列化,支持多种协议,包括 Seata 私有序列化协议、FST、Hessian、Kryo 等。

  • server:服务端模块。用来实现事务协调器,维护全局事务和分支事务的状态,推进事务两阶段提交/回滚。

  • spring:Spring 支持模块。定义了 Seata 事务注解。

  • sqlparser:SQL 解析模块。Seata 使用 Druid SQL 解释器。

  • style:定义了代码规范。

  • tcc:TCC 模式资源管理器模块。用来实现对 TCC 事务模式的支持。

  • test:测试模块。

  • tm:事务协调器模块。定义了全局事务的范围、开启全局事务、提交/回滚全局事务。


逻辑结构

Seata 有 3 个主要角色:TM(Transaction Manager)、RM(Resource Manager)和 TC(Transaction Coordinator)。

其中,TM 和 RM 是以 SDK 的形式作为 Seata 的客户端与业务系统集成在一起,TC 作为 Seata 的服务端独立部署,如下图所示。



TM:事务管理器。与 TC 交互,开启、提交、回滚全局事务。

RM:资源管理器。与 TC 交互,负责资源的相关处理,包括分支事务注册与分支事务状态上报。

TC:事务协调器。维护全局事务和分支事务的状态,推进事务两阶段处理。对于 AT 模式的分支事务,TM 负责事务并发控制。


Seata 处理分布式事务的主要流程如下图所示。

(1)TM 开启全局事务(TM 向 TC 开启全局事务)。

(2)事务参与者通过 RM 与资源交互,并注册分支事务(RM 向 TC 注册分支事务)。

(3)事务参与者在完成资源操作后,上报分支事务状态(RM 向 TC 上报分支事务完成状态)。

(4)TM 结束全局事务,事务一阶段结束(TM 向 TC 提交/回滚全局事务)。

(5)TC 推进事务二阶段操作(TC 向 RM 发起二阶段提交/回滚)。



Seata 事务模式

Seata 支持 4 种事务模式:AT、TCC、Saga、XA。本节做一个简要说明,后面章节会对 AT 模式和 TCC 模式进行深入剖析。

AT 模式

AT 模式是 Seata 主推的分布式事务解决方案,对业务无侵入,真正做到了业务与事务分离,用户只需关注自己的“业务 SQL 语句”。

AT 模式使用起来非常简单,与完全没有使用分布式事务方案相比,业务逻辑不需要修改,只需要增加一个事务注解 @GlobalTransactional 即可,如下图所示。



TCC 模式

TCC 模式需要用户根据自己的业务场景实现 try()、confirm() 和 cancel()这 3 个方法:事务发起方在一阶段执行 try()方法,在二阶段提交执行 confirm()方法,在二阶段回滚执行 cancel()方法。

在 TCC 模式中,Seata 框架把每组 TCC 服务接口当作一个资源(TCC Resource)。这套 TCC 服务接口可以是 RPC,也可以是服务内 JVM 调用。在业务启动时,Seata 框架会自动扫描并识别出 TCC 服务接口的发布方和调用方:

对于发布方,则 Seata 框架会在业务启动时向 TC 注册 TCC Resource。与 AT 模式的 DataSource Resource 一样,每个 TCC Resource 也会带有一个资源 ID。

对于调用方,则 Seata 框架会给其加上切面,在运行时该切面会拦截所有对 TCC 服务接口的调用。每调用一次 try 接口,切面都会先向 TC 注册一个分支事务,然后才去执行 try()方法的业务逻辑并向 TC 汇报分支事务状态。

在请求链路调用完成后,发起方通知 TC 提交或回滚分布式事务,进入二阶段调用流程。此时,TC 会根据之前注册的分支事务,回调对应参与者去执行 TCC 资源的 confirm() 或 cancel()方法。

Seata TCC 框架本身很简单,主要是扫描 TCC 服务接口、注册资源、拦截接口调用、注册分支事务、汇报分支事务状态、回调二阶段接口。对于 TCC 模式来说,最复杂逻辑是 TCC 服务接口的实现。


用户以 TCC 模式接入 Seata 框架,最重要的是考虑如何将自己的业务模型拆成两阶段来实现。


TCC 模式与 AT 模式的主要区别如下。

(1)在使用上,TCC 模式依赖用户自行实现的 3 个方法(try()、confirm()、cancel())成本较大;AT 模式依赖全局事务注解和代理数据源,代码基本不需要改动,对业务无侵入、接入成本极低。

(2)TCC 模式的作用范围在应用层,本质上是实现针对某种业务逻辑的正向和反向方法;AT 模式的作用范围在底层数据源,通过保存操作行记录的前、后镜像和生成反向 SQL 语句进行补偿操作,对上层应用透明。

(3)TCC 模式事务并发控制由业务自行“加锁”,AT 模式由 Seata 框架自动“加锁”。

1.举例

以“扣钱”场景为例,在接入 TCC 模式前,对账户“扣钱”,只需一条更新账户余额的 SQL 语句就能完成;但是在接入 TCC 模式之后,用户则需要考虑如何将原来一步就能完成的“扣钱”操作拆成两阶段,实现成 3 个方法,并且保证如果一阶段 try()方法成功则二阶段 confirm()方法也一定能成功。

如下图所示,try()方法在一阶段执行,需要做资源的检查和预留。在“扣钱”场景下,try()方法要做的是检查账户余额是否充足、预留转账资金(预留的方式就是冻结 A 账户的转账金额)。在 try()方法执行后,账户 A 的余额虽然还是 100 元,但是其中有 30 元已经被冻结了,不能被其他事务使用。

二阶段 confirm()方法执行真正的“扣钱”操作。confirm()方法会使用 try()方法冻结的金额执行账号“扣钱”。在 confirm()方法执行后,账户 A 在一阶段中冻结的 30 元已经被扣除,账户 A 的余额变为 70 元 。



如果二阶段是回滚,则需要在 cancel()方法内释放一阶段 try()方法冻结的 30 元,使账户 A 回到初始状态,100 元全部可用。


相比 AT 模式,TCC 模式对业务代码有很强的侵入性。但是 TCC 模式没有 AT 模式的全局行锁,“加锁”逻辑完全需要根据业务特点制定。


在一些场景下,TCC 模式的性能会比 AT 模式的性能更好。但多数场景下,TCC 模式业务自己实现的“加锁”机制性能不会有明显优势,反而有较大劣势。建议用 AT 模式作为默认方案,用 TCC 模式作为补充方案。‍


Saga 模式

Saga 理论出自 Hector 和 Kenneth 1987 发表的论文 SAGAS。

Saga 是一种补偿协议。在 Saga 模式中,在分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。

如下图所示,T1~T3 都是正向的业务流程,都对应着一个冲正逆向操作 C1~C3。



在分布式事务执行过程中,会依次执行各参与者的正向操作。

如果所有正向操作均执行成功,则分布式事务提交。

如果任何一个正向操作执行失败,则分布式事务会退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。

Saga 模式的正向服务与补偿服务也需要业务开发者实现,因此也具有很强的业务侵入性。在 Saga 模式中,分布式事务通常是由事件驱动的,在各个参与者之间是异步执行的。

Saga 模式是一种长事务解决方案,适用于业务流程长且需要保证事务最终一致性的业务系统。Saga 模式的一阶段就会提交本地事务,在无锁、长流程情况下这可以保证性能。

Saga 模式的优势:

在一阶段提交本地数据库事务,无锁,高性能。

参与者可以采用事件驱动异步执行,高吞吐。

补偿服务即正向服务的“反向”操作,易于理解,易于实现。

Saga 模式也存在很明显的缺点:在一阶段已经提交了本地数据库事务,且没有进行“预留”动作,所以不能保证隔离性,不容易进行并发控制。与 AT 模式和 TCC 模式相比,Saga 模式的适用场景有限。

XA 模式

在 XA 模式中,需要在 Seata 定义的分布式事务范围内,利用事务资源(数据库、消息服务等)实现对 XA 协议的支持,以 XA 协议的机制来管理分支事务。


本质上,Seata 另外的 3 大事务模式(AT、TCC、Saga)都是补偿型的。事务处理机制构建在框架或应用中。事务资源本身对分布式事务是无感知的。


而在 XA 模式下,事务资源对分布式事务是可感知的。


XA 协议要求事务资源本身提供对规范和协议的支持。因为事务资源(数据库、消息队列)可感知并参与分布式事务处理过程,所以事务资源可以保障从任意视角对数据的访问进行有效隔离,满足全局数据一致性。


XA 模式是传统的分布式强一致性的解决方案,性能较低,在实际业务中使用得较少,本书不做深入探讨。


*本文节选自

《正本清源分布式事务之 Seata(全彩)》一书

Seata 作者编写,分布式事务处理

欢迎阅读本书了解更多精彩内容!



用户头像

还未添加个人签名 2019.10.21 加入

还未添加个人简介

评论

发布
暂无评论
详解阿里开源分布式事务框架Seata