Seata 入门教程 - 基础篇(2020 年)

大纲

前言

官方资源

专业术语

  • TX 协议:应用或者应用服务器与事务管理器的接口

  • XA 协议:全局事务管理器与资源管理器的接口。XA 是由 X/Open 组织提出的分布式事务规范,该规范主要定义了全局事务管理器和局部资源管理器之间的接口,主流的数据库产品都实现了 XA 接口。XA 接口是一个双向的系统接口,在事务管理器以及多个资源管理器之间作为通信桥梁。之所以需要 XA 是因为在分布式系统中从理论上讲两台机器是无法达到一致性状态的,因此引入一个单点进行协调。由全局事务管理器管理和协调的事务可以跨越多个资源和进程。全局事务管理器一般使用 XA 二阶段协议与数据库进行交互。

分布式理论

CAP 理论

CAP 定理是由加州大学伯克利分校 Eric Brewer 教授提出来的,他指出 WEB 服务无法同时满足一下三个属性:

  • 一致性 (Consistency):客户端知道一系列的操作都会同时发生 (生效)
  • 可用性 (Availability):每个操作都必须以可预期的响应结束
  • 分区容错性 (Partition tolerance):即使出现单个组件无法可用,操作依然可以完成

具体地讲在分布式系统中,任何数据库设计或者 Web 应用至多只能同时支持上面的两个属性。显然,任何横向扩展策略都要依赖于数据分区。因此,设计人员必须在一致性与可用性之间做出选择

BASE 理论

在分布式系统中,往往追求的是可用性,它的重要程序比一致性要高,那么如何实现高可用性呢?前人已经给我们提出来了另外一个理论,就是 BASE 理论,它是用来对 CAP 定理进行进一步扩充的。BASE 理论指的是:

  • Basically Available(基本可用)
  • Soft state(软状态)
  • Eventually consistent(最终一致性)

BASE 理论是对 CAP 中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)

酸碱平衡

ACID 能够保证事务的强一致性,即数据是实时一致的,这在本地事务中是没有问题的。在分布式事务中,强一致性会极大影响分布式系统的性能,因此分布式系统中遵循 BASE 理论即可。但分布式系统的不同业务场景对一致性的要求也不同。如交易场景下,就要求强一致性,此时就需要遵循 ACID 理论,而在注册成功后发送短信验证码等场景下,并不需要实时一致,因此遵循 BASE 理论即可。因此要根据具体业务场景,在 ACID 和 BASE 之间寻求平衡。

分布式事务基础

什么是事务

事务指的就是一个操作单元,在这个操作单元中的所有操作最终要保持一致的行为,要么所有操都成功,要么所有的操作都被撤销。简单地说,事务提供一种” 要么什么都不做,要么做全套 “机制。

什么是本地事务

本地事务其实可以认为是数据库提供的事务机制。说到数据库事务就不得不说,数据库事务中的四大特性(ACID):

  • A:原子性(Atomicity),一个事务中的所有操作,要么全部完成,要么全部不完成
  • C:一致性(Consistency),在一个事务执行之前和执行之后数据库都必须处于一致性状态
  • I:隔离性(Isolation),在并发环境中,当不同的事务同时操作相同的数据时,事务之间互不影响
  • D:持久性(Durability),指的是只要事务成功结束,它对数据库所做的更新就必须永久的保存下来

数据库事务在实现时会将一次事务涉及的所有操作全部纳入到一个不可分割的执行单元,该执行单元中的所有操作要么都成功,要么都失败,只要其中任一操作执行失败,都将导致整个事务的回滚。

什么是分布式事务

分布式事务指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简而言之,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。一句话概括就是,一次业务操作需要跨多个数据源或者需要跨多个系统进行远程调用,就会产生分布式事务问题。

分布式事务的适用场景

  • 单体系统访问多个数据库:一个服务需要调用多个数据库实例完成数据的增删改操作

distributed-transaction-scene-1

  • 多个微服务访问同一个数据库:多个服务需要调用一个数据库实例完成数据的增删改操作

distributed-transaction-scene-2

  • 多个微服务访问多个数据库:多个服务需要调用多个数据库实例完成数据的增删改操作

distributed-transaction-scene-3

分布式事务解决方案

全局事务(DTP)

全局事务是基于 DTP 模型实现的,DTP 是由 X/Open 组织提出的一种分布式事务模型 — X/Open Distributed Transaction Processing Reference Model。它规定了要实现分布式事务,需要三种角色:

  • AP:Application 应用系统(微服务)
  • RM:Resource Manager 资源管理器(数据库)
  • TM:Transaction Manager 事务管理器(全局事务管理)

整个事务分成两个阶段:

  • 阶段一:表决阶段(投票阶段),所有参与者都将本事务执行预提交,并将能否成功的信息反馈发给协调者
  • 阶段二:执行阶段(提交阶段),协调者根据所有参与者的反馈,通知所有参与者,步调一致地执行提交或者回滚

distributed-transaction-dtp-1


distributed-transaction-dtp-2

优点:

  • 提高了数据一致性的概率,实现成本较低

缺点:

  • 单点问题:事务协调者宕机
  • 同步阻塞:延迟了提交时间,加长了资源阻塞时间
  • 数据不一致:在提交的第二阶段,依然存在 Commit 结果未知的情况,有可能导致数据不一致(即两阶段提交无法解决的问题)

两阶段提交(2PC)

分布式系统的一个难点是如何保证架构下多个节点在进行事务性操作的时候保持一致性。为实现这个目的,两阶段提交算法的成立基于以下假设:

  • 该分布式系统中,存在一个节点作为协调者(Coordinator),其他节点作为参与者(Cohorts),且节点之间可以进行网络通信
  • 所有节点都采用预写式日志,且日志被写入后即被保持在可靠的存储设备上,即使节点损坏不会导致日志数据的消失
  • 所有节点不会永久性损坏,即使损坏后仍然可以恢复

第一阶段(投票阶段):

  • 协调者节点向所有参与者节点询问是否可以执行提交操作(vote),并开始等待各参与者节点的响应
  • 参与者节点执行询问发起为止的所有事务操作,并将 Undo 信息和 Redo 信息写入日志(注意:如果成功,这里其实每个参与者已经执行了事务操作)
  • 各参与者节点响应协调者节点发起的询问,如果参与者节点的事务操作实际执行成功,则它返回一个” 同意” 消息;如果参与者节点的事务操作实际执行失败,则它返回一个” 中止” 消息

第二阶段(提交阶段):

当协调者节点从所有参与者节点获得的相应消息都为” 同意” 时:

  • 协调者节点向所有参与者节点发出” 正式提交(Commit)” 的请求
  • 参与者节点正式完成操作,并释放在整个事务期间内占用的资源
  • 参与者节点向协调者节点发送” 完成” 消息
  • 协调者节点受到所有参与者节点反馈的” 完成” 消息后,完成事务

中断事务的发生:

如果任一参与者节点在第一阶段返回的响应消息为” 中止”,或者协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时:

  • 协调者节点向所有参与者节点发出” 回滚操作(Rollback)” 的请求
  • 参与者节点利用之前写入的 Undo 信息执行回滚,并释放在整个事务期间内占用的资源
  • 参与者节点向协调者节点发送” 回滚完成” 消息
  • 协调者节点受到所有参与者节点反馈的” 回滚完成” 消息后,取消事务
  • 特别注意:不管最后结果如何,第二阶段都会结束当前事务

两阶段提交的缺点:

  • 资源阻塞:执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态
  • 参与者发生故障:协调者需要给每个参与者额外指定超时机制,超时后整个事务失败(没有多少容错机制)
  • 协调者发生故障:参与者会一直阻塞下去,需要额外的备机进行容错(这个可以依赖 Paxos 协议实现 HA)
  • 两阶段提交无法解决的问题:协调者在发出 Commit 消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否已经被提交成功,这就有可能导致数据不一致

三阶段提交(3PC)

与两阶段提交不同的是,三阶段提交有两个改动点:

  • 引入超时机制。同时在协调者和参与者中都引入超时机制
  • 在第一阶段和第二阶段中插入一个准备阶段,保证了在最后提交阶段之前各参与节点的状态是一致的

也就是说,除了引入超时机制之外,3PC 把 2PC 的投票阶段再次一分为二,这样三阶段提交就有 CanCommit、PreCommit、DoCommit 三个阶段。

CanCommit 阶段:

3PC 的 CanCommit 阶段其实和 2PC 的投票阶段很像,协调者向参与者发送 Commit 请求,参与者如果可以提交就返回 Yes 响应,否则返回 No 响应:

  • 事务询问:协调者向参与者发送 CanCommit 请求,询问是否可以执行事务提交操作,然后开始等待参与者的响应
  • 响应反馈:参与者接到 CanCommit 请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返回 Yes 响应,并进入预备状态,否则返回 No 响应

PreCommit 阶段:

协调者根据参与者的响应情况来决定是否可以执行事务的 PreCommit 操作。根据响应情况,有以下两种可能:

  • 假如协调者从所有的参与者获得的反馈都是 Yes 响应,那么就会执行事务的预执行

    • 发送预提交请求:协调者向参与者发送 PreCommit 请求后,并进入 Prepared 阶段
    • 事务预提交:参与者接收到 PreCommit 请求后,会执行事务操作,并将 Undo 和 Redo 信息记录到事务日志中
    • 响应反馈:如果参与者成功的执行了事务操作,则返回 ACK 响应,同时开始等待最终指令
  • 假如有任何一个参与者向协调者发送了 No 响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断

    • 发送中断请求:协调者向所有参与者发送 Abort 请求
    • 中断事务:参与者收到来自协调者的 Abort 请求之后(或超时之后,仍未收到协调者的请求),执行事务的中断

Undo 和 Redo 日志

  • (1) Undo 和 Redo 日志是用于事务回滚和数据恢复的重要机制。
  • (2) Undo 日志:记录在事务操作前的数据状态,以便在需要回滚事务时,可以撤销已执行的操作,恢复到事务开始前的状态。举例来说,如果事务在某个步骤中修改了数据库表中的数据,Undo 日志就会在执行修改之前记录原始数据值。这样如果事务最终没有提交成功,系统可以通过这些日志信息将数据还原到修改前的状态。
  • (3) Redo 日志:记录事务操作后的数据状态,以便在事务失败需要恢复时重新应用这些操作。当事务最终准备提交时,Redo 日志会确保所有的更改都能被重复应用,以达到一致的提交效果。举例来说,如果事务已经提交,但系统在写入数据到持久性存储时发生了崩溃,那么通过 Redo 日志,就可以在系统恢复后重新执行已提交的操作,确保数据一致性。值得一提的是,Redo 是单词 do again 的含义,表示 重做 或者 再次执行 的意思。

DoCommit 阶段

该阶段进行真正的事务提交,也可以分为以下两种情况:

  • 执行提交:

    • 发送提交请求:协调接收到参与者发送的 ACK 响应,那么它将从预提交状态进入到提交状态,并向所有参与者发送 DoCommit 请求
    • 事务提交:参与者接收到 DoCommit 请求之后,执行正式的事务提交,并在完成事务提交之后释放所有事务资源
    • 响应反馈:事务提交完之后,向协调者发送 ACK 响应
    • 完成事务:协调者接收到所有参与者的 ACK 响应之后,完成事务
  • 中断事务

    • 发送中断请求:协调者向所有参与者发送 Abort 请求
    • 事务回滚:参与者接收到 Abort 请求之后,利用其在阶段二记录的 Undo 信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源
    • 反馈结果:参与者完成事务回滚之后,向协调者发送 ACK 消息
    • 中断事务:协调者接收到参与者反馈的 ACK 消息之后,执行事务的中断

这里协调者如果没有接收到参与者发送的 ACK 响应(可能是接受者发送的不是 ACK 响应,也可能响应超时),那么就会执行中断事务。

TCC(两阶段型、补偿型)

TCC(Try Confirm Cancel)属于补偿型分布式事务(又被称为补偿事务),类似 2PC 的柔性分布式解决方案,属于 2PC 的改良版。TCC 实现分布式事务一共有三个步骤:

  • Try(尝试待执行的业务):这个过程并未执行业务,只是完成所有业务的一致性检查,并预留好执行所需的全部资源

  • Confirm(确认执行业务):确认执行业务操作,不做任何业务检查,只使用 Try 阶段预留的业务资源。通常情况下,采用 TCC 则认为 Confirm 阶段是不会出错的。即只要 Try 成功,Confirm 就一定成功。若 Confirm 阶段真的出错了,需引入重试机制或人工处理

  • Cancel(取消待执行的业务):取消 Try 阶段预留的业务资源。通常情况下,采用 TCC 则认为 Cancel 阶段也是一定成功的。若 Cancel 阶段真的出错了,需引入重试机制或人工处理

tcc-transaction

TCC 两阶段提交与 XA 两阶段提交的区别:

  • XA 是资源层面的分布式事务,强一致性,在两阶段提交的整个过程中,会一直持有资源的锁
  • TCC 是业务层面的分布式事务,最终一致性,不会一直持有资源的锁

TCC 事务的优缺点:

  • 优点:把数据库层的两阶段提交上提到了应用层来实现,规避了数据库层的 2PC 性能低下的问题
  • 缺点:TCC 的 Try、Confirm 和 Cancel 操作功能需业务提供,开发成本高

最大努力通知(定期校对)

最大努力通知也被称为定期校对,其实是对第二种解决方案的进一步优化。它引入了本地消息表来记录错误消息,然后加入失败消息的定期校对功能,来进一步保证消息会被下游系统消费。

best-effort-notice

第一步:消息由系统 A 投递到消息中间件

  • 1)处理业务的同一事务中,向本地消息表中写入一条记录
  • 2)准备专门的消息发送者不断地发送本地消息表中的消息到消息中间件,如果发送失败则重试

第二步:消息由中间件投递到系统 B

  • 1)消息中间件收到消息后负责将该消息同步投递给相应的下游系统,并触发下游系统的任务执行
  • 2)当下游系统处理成功后,向消息中间件反馈确认应答,消息中间件便可以将该条消息删除,从而该事务完成
  • 3)对于投递失败的消息,利用重试机制进行重试,对于重试失败的,写入错误消息表
  • 4)消息中间件需要提供失败消息的查询接口,下游系统会定期查询失败消息,并将其消费

优缺点:

  • 优点: 一种非常经典的实现,实现了最终一致性
  • 缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理,并且在业界并没有成熟的方案来解决

基于可靠消息服务实现最终一致性

基于可靠消息服务的方案是通过消息中间件(如 Kafka、RocketMQ)保证上、下游应用数据操作的最终一致性。假设有 A 和 B 两个系统,分别可以处理任务 A 和任务 B。此时存在一个业务流程,需要将任务 A 和任务 B 在同一个事务中处理,此时就可以使用消息中间件来实现这种分布式事务。

reliable-message-service

第一步:消息由系统 A 投递到消息中间件

  • 1)在系统 A 处理任务 A 前,首先向消息中间件发送一条消息
  • 2)消息中间件收到后将该条消息持久化,但并不投递。持久化成功后,向 A 回复一个确认应答
  • 3)系统 A 收到确认应答后,则可以开始处理任务 A
  • 4)任务 A 处理完成后,向消息中间件发送 Commit 或者 Rollback 请求。该请求发送完成后,对系统 A 而言,该事务的处理过程就结束了
  • 5)如果消息中间件收到 Commit,则向 B 系统投递消息;如果收到 Rollback,则直接丢弃消息。但是如果消息中间件收不到 Commit 和 Rollback 指令,那么就要依靠” 超时询问机制”

超时询问机制

系统 A 除了实现正常的业务流程外,还需提供一个事务询问的接口,供消息中间件调用。当消息中间件收到发布消息便开始计时,如果到了超时没收到确认指令,就会主动调用系统 A 提供的事务询问接口询问该系统目前的状态。该接口会返回三种结果,中间件根据三种结果做出不同反应:

  • 提交:将该消息投递给系统 B
  • 回滚:直接将消息丢弃
  • 处理中:继续等待

第二步:消息由中间件投递到系统 B

消息中间件向下游系统投递完消息后便进入阻塞等待状态,下游系统便立即进行任务的处理,任务处理完成后便向消息中间件返回应答。

  • 如果消息中间件收到确认应答后便认为该事务处理完毕
  • 如果消息中间件在等待确认应答超时之后就会重新投递,直到下游消费者返回消费成功响应为止。一般消息中间件可以设置消息重试的次数和时间间隔,如果最终还是不能成功投递,则需要手工干预。这里之所以使用人工干预,而不是使用让 A 系统回滚,主要是考虑到整个系统设计的复杂度问题

基于可靠消息服务的分布式事务,前半部分使用异步,注重性能;后半部分使用同步,注重开发成本。

Seata 介绍

Seata 的简介

2019 年 1 月,阿里巴巴中间件团队发起了开源项目 Fescar(Fast & Easy Commit And Rollback),其愿景是让分布式事务的使用像本地事务的使用一样,简单和高效,并逐步解决开发者们遇到的分布式事务方面的所有难题。Fescar 开源后,蚂蚁金服加入 Fescar 社区参与共建,并在 Fescar 0.4.0 版本中贡献了 TCC 模式。为了打造更中立、更开放、生态更加丰富的分布式事务开源社区,经过社区核心成员的投票,决定对 Fescar 进行品牌升级,于 2019 年 5 月 开始更名为 Seata,意为:Simple Extensible Autonomous Transaction Architecture,是一套一站式分布式事务解决方案,为用户提供了 AT、TCC、SAGA 和 XA 事务模式。Seata 融合了阿里巴巴和蚂蚁金服在分布式事务技术上的积累,并沉淀了新零售、云计算和新金融等场景下丰富的实践经验,但要实现适用于所有的分布式事务场景的愿景,仍有很长的路要走。更多介绍可参考:Seata 项目Seata 官方示例代码Seata 官网Seata 官方中文文档

Seata 的演进历史

  • (1) 早在 2007 年,阿里巴巴和蚂蚁集团内部开发了分布式事务中间件,用于解决电商、支付、物流等业务场景中应用数据的一致性问题。内部项目分别被称为 TXC(Taobao Transaction Constructor) / XTS(eXtended Transaction Service),该项目几乎在每笔订单的交易支付链路几乎都有使用。
  • (2) 自 2013 年以来,阿里巴巴和蚂蚁集团已在阿里云和金融云上向企业客户分别发布了分布式事务云服务产品 GTS(Global Transaction Service) / DTX (Distributed Transaction-eXtended),在各个行业领域积累了大量用户。
  • (3) 2019 年 1 月,阿里巴巴集团正式开源了该项目,项目命名为 Fescar(Fast & Easy Commit and Rollback)。项目开源以来,它受到了众多开发人员的热烈欢迎和赞扬,开源一周收获了超 3k star,曾一度蝉联 GitHub Trending 排行榜第一。
  • (4) 2019 年 4 月,蚂蚁集团数据中间件团队加入了 Fescar 社区。为了创建一个更加开放和中立的社区,Fescar 改名为 Seata(Simple Extensible Autonomous Transaction Architecture),代码仓库从 Alibaba Organization 迁移到其独立的 Seata Organization。
  • (5) 2019 年 12 月,Seata 开源项目正式发布 1.0.0 GA 版本,标志着项目已基本可生产使用。
  • (6) 2023 年 10 月,为了更好的通过社区驱动技术的演进,阿里和蚂蚁集团正式将 Seata 捐赠给 Apache 基金会,该提案已通过了 Apache 基金会的投票决议,Seata 正式进入 Apache 孵化器。

Seata 的设计理念

Seata 的设计目标是对业务无侵入,因此从业务无侵入的 2PC 方案着手,在传统 2PC 的基础上改良。它把一个分布式事务理解成一个包含了若干分支事务的全局事务。全局事务的职责是协调其下管辖的分支事务达成一致,要么一起成功提交,要么一起失败回滚。此外,通常分支事务本身就是一个关系型数据库的本地事务。

seata-architecture

Seata 的三大组件

  • TC(Transaction Coordinator):事务协调器,维护全局和分支事务的状态,负责协调并驱动全局事务的提交或回滚
  • TM(Transaction Manager):事务管理器,控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议
  • RM(Resource Manager):资源管理器,负责管理分支事务上的资源,向 TC 注册分支事务,上报分支事务的状态,接受 TC 的命令来提交或者回滚分支事务

seata-modules

XID 是全局事务的唯一标识,它可以在服务的调用链路中传递,绑定到服务的事务上下文中。

总结

  • TC(事务协调器):就是 Seata 自身,负责维护全局事务和分支事务的状态,驱动全局事务提交或回滚。
  • TM(事务管理器):就是标注全局事务注解 @GlobalTransactional 启动入口动作的微服务模块(比如订单模块),它是事务的发起者,负责定义全局事务的范围,并根据 TC 维护的全局事务和分支事务状态,作出开启全局事务、提交全局事务、回滚全局事务的决议。
  • RM(资源管理器):就是 MySQL 数据库本身,可以有多个 RM,负责管理分支事务上的资源,向 TC 注册分支事务,上报分支事务状态,接受 TC 的命令来提交或者回滚分支事务。

Seata 的执行流程

Seata 三大组件会相互协作运行,TC 以 Seata 服务器(Server)形式独立部署,TM 和 RM 则是以 Seata Client 的形式集成在微服务应用中运行。Seata 的具体执行流程如下:

  • 1)A 服务的 TM 向 TC 申请开启一个全局事务,TC 就会创建一个全局事务并返回一个唯一的 XID
  • 2)A 服务的 RM 向 TC 注册分支事务,并将其纳入 XID 对应全局事务的管辖
  • 3)A 服务执行分支事务,向数据库执行操作
  • 4)A 服务开始远程调用 B 服务,此时 XID 会在微服务的调用链上传播
  • 5)B 服务的 RM 向 TC 注册分支事务,并将其纳入 XID 对应的全局事务的管辖
  • 6)B 服务执行分支事务,向数据库执行操作
  • 7)全局事务调用链处理完毕,TM 根据有无异常向 TC 发起全局事务的提交或者回滚决议
  • 8)TC 协调其管辖之下的所有分支事务,决定是否回滚

总结

  • (1) TM 向 TC 申请开启一个全局事务,全局事务创建成功后会生成一个全局唯一的 XID
  • (2) XID 在微服务调用链路的上下文中传播
  • (3) RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖
  • (4) TM 向 TC 发起针对 XID 的全局提交或回滚决议
  • (5) TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求

Seata 的 ORM 框架支持

Seata 虽然是保证数据一致性的组件,但对于 ORM 框架并没有特殊的要求,像主流的 Mybatis、Mybatis-Plus、Spring Data JPA、Hibernate 等都支持。这是因为 ORM 框架位于 JDBC 结构的上层,而 Seata 的 AT、XA 事务模式是对 JDBC 标准接口操作的拦截和增强。

Seata 实现的 2PC 与传统 2PC 的区别

  • 1)架构层次方面:传统 2PC 方案的 RM 实际上是在数据库层,RM 本质上就是数据库自身,通过 XA 协议实现,而 Seata 的 RM 是以 Jar 包的形式作为中间件层部署在应用程序这一侧的
  • 2)两阶段提交方面:传统 2PC 无论第二阶段的决议是 Commit 还是 Rollback,事务性资源的锁都要保持到 Phase2(阶段二) 完成才释放。而 Seata 的做法是在 Phase1(阶段一) 就将本地事务提交,这样就可以省去 Phase2(阶段二) 持锁的时间,整体提高了效率

Seata Server 安装

Seata 分 TC、TM 和 RM 三个角色,TC(Server 端)需要单独作为服务端部署,TM 和 RM(Client 端)由业务系统集成(如 Maven、Gradle)。

Seata Server 下载

1)Seata Server 的官方下载地址在这里,直接下载已编译好的二进制包(seata-server-1.4.0.tar.gz ),然后解压即可使用

1
2
3
4
5
# 下载
$ wget https://github.com/seata/seata/releases/download/v1.4.0/seata-server-1.4.0.tar.gz

# 解压
$ tar -xvf seata-server-1.4.0.tar.gz

2)Seata 的初始化资源的官方下载地址在这里,需要下载 Seata 的源代码包(Source code),后面初始化数据库或者配置中心时会用到资源目录里的文件

1
2
3
4
5
6
7
8
9
10
11
# 下载
$ wget https://github.com/seata/seata/archive/v1.4.0.tar.gz

# 解压
# tar -xvf v1.4.0.tar.gz

# 资源目录的结构
seata-1.4.0/script
├── client
├── config-center
└── server

资源目录说明如下:

  • server:Server 端数据库脚本及各个容器配置
  • client:存放 Client 端的 SQL 脚本、参数配置
  • config-center:各个配置中心参数导入脚本,其中的 config.txt(包含 Server 和 Client,原名为 nacos-config.txt) 为通用参数文件

Seata Server 配置

1)将 Seata Server(TC)的存储模式更改为 DB,即使用数据库来存储全局事务会话信息,同时自定义事务组的名称。这里演示使用的数据库为 MySQL,默认支持的数据库类型包括:MySQL、Oracle、PostgreSQL、H2、Oceanbase,其中 service.vgroupMapping 的详细介绍可以看自定义事务组的名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 备份配置文件
$ cp seata/conf/file.conf seata/conf/file.conf.bak

# 编辑配置文件,更改或者新增以下内容
$ vim seata/conf/file.conf

service {
vgroupMapping.tx_group_test = "default" #自定义事务组的名称,若不存在service配置项,直接新增对应的配置内容即可
default.grouplist = "127.0.0.1:8091"
enableDegrade = false
disable = false
max.commit.retry.timeout = "-1"
max.rollback.retry.timeout = "-1"
disableGlobalTransaction = false
}

store {
mode = "db" # 存储模式

db {
dbType = "mysql" # 数据库类型
datasource = "druid" # 数据库连接池
driverClassName = "com.mysql.cj.jdbc.Driver" # 数据库驱动
url = "jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false" # 数据库连接地址
user = "mysql" # 数据库用户名
password = "mysql" # 数据库密码
}
}

2)初始化 Seata Server(TC)依赖的 MySQL 数据库,用于存储全局事务会话信息,SQL 初始化脚本的位置是 Seata 源码目录下的 script/server/db/mysql.sql。全局事务会话信息由三块内容构成,全局事务 –> 分支事务 –> 全局锁,对应的表分别是 global_tablebranch_tablelock_table

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 创建Seata数据库
mysql> create database seata default character set utf8;

# 切换数据库
mysql> use seata;

# 执行SQL初始化脚本
mysql> source seata-1.4.0/script/server/db/mysql.sql

# 查看数据库表
mysql> show tables;
+-----------------+
| Tables_in_seata |
+-----------------+
| branch_table |
| global_table |
| lock_table |
+-----------------+
3 rows in set (0.00 sec)

3)指定 Seata Server(TC)依赖的注册中心,这里使用的注册中心是 Nacos。为了演示方便,这里不再使用配置中心来存储 TC 的相关配置,即直接使用本地的 file.conf 配置文件。默认支持的注册中心与配置中心列表如下:

  • 配置中心支持类型:File、Nacos 、Apollo、Zookeeper、Consul、Etcd3
  • 注册中心支持类型:File 、Nacos 、Eureka、Zookeeper、Consul、Etcd3、Sofa、Redis
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 备份配置文件
$ cp seata/conf/registry.conf seata/conf/registry.conf.bak

# 编辑配置文件,更改或者新增以下内容
$ vim seata/conf/registry.conf

registry {
type = "nacos"

nacos {
application = "seata-server"
serverAddr = "127.0.0.1:8848"
group = "SEATA_GROUP"
namespace = ""
cluster = "default"
username = ""
password = ""
}
}

config {
type = "file"

file {
name = "file.conf"
}
}

Seata Server 启动

先将 Seata Server 依赖的数据库、注册中心服务启动了,最后才启动 Seata Server。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 创建GC的日志目录
$ mkdir seata/logs

# 进入bin目录
$ cd seata/bin

# 执行启动脚本
$ sh seata-server.sh

# 或者后台启动
$ nohup sh seata-server.sh &

# 或者指定启动参数
$ sh seata-server.sh -h 127.0.0.1 -p 8091 -n 1

启动参数说明如下:

  • -h: 注册到注册中心的 IP
  • -p: Seata Server 的本地监听端口,默认端口是 8091
  • -m: 全局事务会话信息存储模式,file、db、redis,优先读取启动参数 (Seata-Server 1.3 及以上版本支持 Redis)
  • -n: Server Node,多个 Server 时,需区分各自节点,用于生成不同区间的 transactionId,以免冲突
  • -e: 多环境配置可以参考这里

特别注意

堆内存建议分配 2G,堆外内存 1G,JVM 的内存参数可以直接在 seata/bin/-server.sh 脚本里调整。

Seata Server 成功启动后,在注册中心的服务列表里,可以看到 Seata Server 的服务已经成功注册:

seata-nacos-service

Seata Server 配置介绍

配置文件说明

  • registry.conf:用于指定 Seata Server(TC) 的注册中心和获取配置信息的方式,默认获取方式是 file。如果使用了其他注册中心,可以将 Seata Server 自身也注册到注册中心(如 Nacos)
  • file.conf:用于指定 Seata Server(TC)的相关配置。如果使用了配置中心,可以将 file.conf 文件里的配置信息都存储到配置中心(如 Nacos)

配置中心使用

若在 registry.conf 中指定使用配置中心来存储 TC 的相关配置(如下),即利用配置中心来替代 file.conf 配置文件,那么此时需要手动将 file.conf 里的配置信息添加到配置中心

1
2
3
4
5
6
7
8
9
10
11
12
13
config {
type = "nacos"

nacos {
serverAddr = "127.0.0.1:8848"
namespace = ""
group = "SEATA_GROUP"
username = ""
password = ""
}

...
}

Seata 官方提供了将配置信息批量写入到各种主流配置中心的 Shell 脚本,存放路径是在 Seata 源码目录下的 script/config-center 目录(如下)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
script/config-center
├── apollo
│   └── apollo-config.sh
├── config.txt
├── consul
│   └── consul-config.sh
├── etcd3
│   └── etcd3-config.sh
├── nacos
│   ├── nacos-config.py
│   └── nacos-config.sh
├── README.md
└── zk
└── zk-config.sh

其中 config.txt 为通用参数文件,包含了 Seata Server 需要的所有配置信息,只需执行对应的 Shell 脚本将配置信息写入到配置中心即可。值得一提的是,config.txt 文件必须在 xxxx.sh 的上级目录里;若使用 Nacos 作为配置中心,执行脚本时可以指定一些启动参数,如 Nacos 的 IP、端口号、命名空间、配置组等,Shell 脚本的具体使用方法可以查看 script/config-center/README.md 说明文档。

1
$ nacos-config.sh -h 127.0.0.1 -p 8848 -t namespace -g group -u username -w password

成功批量导入配置信息到配置中心后,控制台会输出如下提示:

1
2
3
4
=========================================================================
Complete initialization parameters, total-count:79 , failure-count:0
=========================================================================
Init nacos config finished, please start seata-server.

访问 Nacos 的控制台,可以看到已经有对应的配置信息

seata-inport-to-config-center

配置 TC 的存储模式

Seata Server(TC)的存储模式现有 File、DB、Redis 三种(后续将引入 Raft、Mongodb),需要在 file.conf 配置文件中指定(如下)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
store {
mode = "file"

## file store property
file {
## store location dir
dir = "sessionStore"
# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
maxBranchSessionSize = 16384
# globe session size , if exceeded throws exceptions
maxGlobalSessionSize = 512
# file buffer size , if exceeded allocate new buffer
fileWriteBufferCacheSize = 16384
# when recover batch read size
sessionReloadReadSize = 100
# async, sync
flushDiskMode = async
}

...
}

默认存储模式为 File,若使用 File 模式则无需改动任何配置,直接启动即可,每种模式的说明如下:

  • File 模式为单机模式,全局事务会话信息在内存中读写,并持久化为本地文件 root.data,性能较高
  • DB 模式为高可用模式,全局事务会话信息通过 DB 共享,性能会差一点
  • Redis 模式在 Seata-Server 1.3 及以上版本开始支持,性能较高,存在事务信息丢失风险,需要配置适合当前业务场景的 Redis 持久化策略

自定义事务组的名称

特别注意,file.conf 中的 service.vgroupMapping 这个配置,在 Spring Cloud 中的值默认是 ${spring.application.name}-fescar-service-group,可以通过指定 application.yml 中的 spring.cloud.alibaba.seata.tx-service-group 这个属性来覆盖;但是必须要和 file.conf 中的 service.vgroupMapping 一致,否则会出现 no available service 'null' found, please make sure registry config correct 的错误,举例说明如下:

1
2
3
service {
vgroupMapping.tx_group_test = "default"
}
1
2
3
4
5
spring:
cloud:
alibaba:
seata:
tx-service-group: tx_group_test

在上述的配置中,Spring Cloud 中 tx-service-group 的值也必须为 tx_group_test;如果将 vgroupMapping.xxxx 中的 xxxx(Key 值)改为 abcdefg,则 Spring Cloud 中 tx-service-group 的值也必须为 abcdefg,即这两个值必须保持一致

Seata Server 的坑

default.grouplist 属性

在 Seata Server 的 file.conf 配置文件中,有个 default.grouplist 配置,该配置的使用说明如下:

  • 1)只有在 registry.conf 中配置了 registry.type=file,即注册中心是 File 模式时,该配置才会起作用
  • 2)对应的值可以配置多个,配置多个就需要搭建 Seata Server 集群。由于默认并未提供本地文件的同步功能,所以在 store.mode=file 模式下,这种集群方式的配置会报错;如果 Seata Server 搭建为集群,建议配置为 store.mode=db,这样就可以通过 DB 来共享 TC(Seata Server) 集群间的数据
  • 3)当 registry.type=file 时,这个 default.grouplist 才会起作用,但是 File 方式并不能提供一个注册中心的完整功能,比如健康检查机制,实例列表的更新剔出等,建议选择 Nacos 、Eureka、Redis、Zookeeper、Consul、Etcd3、Sofa 作为注册中心
  • 4)registry.type=fileconfig.type=file 的设计初衷,是让开发者在不依赖第三方注册中心或配置中心的前提下,可以通过 File 这种简单的直连快速验证 Seata 服务,达到快速上手的目的

service.vgroup_mapping 属性

Seata Server <=1.0 的版本用的是 service.vgroup_mapping,但在新版本里改成了 service.vgroupMapping。若应用启动后无法连接 Seata Server,且抛出了以下异常信息,此时应该注意使用的是不是旧的 service.vgroup_mapping

1
no available service 'null' found, please make sure registry config correct

file.conf 配置文件中,service.vgroupMapping 支持配置多个:

1
2
3
4
service.vgroupMapping.user-service-group=default
service.vgroupMapping.order-service-group=default
service.vgroupMapping.account-service-group=default
service.vgroupMapping.storage-service-group=default

参考资料