版权声明 :

本文为博主原创文章,如需转载,请注明出处(https://blog.csdn.net/F1004145107/article/details/86648263)

一、 什么是分布式事务

  • 在进入微服务系统之后,给我们带来了很多的方便,同时也有很多的痛点,这里我们不讨论微服务系统在后期逐渐变得'臃肿'的问题,本文主要讨论分布式事务

  • 在以前的系统中,我们将订单、账户等服务写在同一个服务内,用的是同一个本地数据源,本地事务可以保证这俩个操作是同时提交(commit)或者回滚(rollback),但是在微服务系统中,我们将俩个服务单独部署,并且数据源也不是同一个,此时就会有一个问题,订单的事务提交了,但是账户因为异常原因回滚了,此时会造成用户已经成功购买了商品,但是钱并没有扣除,由此可见,分布式事务解决的就是将俩个本地事务当作一个本地事务来处理,一起提交或一起回滚

CAP

  • 在了解分布式事务的解决方案之前我们需要先明白,分布式事务的基本原则

  • 一致性(Consistency) : 在执行完该流程的操作后,所有的更新操作是否能保证整个系统的数据都被更新,保证数据的一致性

  • 可用性(Availability) : 每个操作能够在正常的响应时间内完成,并给客户端返回正常的数据

  • 分区容错性(Partition Tolerance) : 当系统中某个服务宕机之后,任然能满足一和可用性的条件,并且不会影响用户使用

 二、 现有的分布式事务解决方案

分布式事务的发展

  • XA - 俩阶段提交协议(业务无侵入)

    • 第一阶段 : 事务管理器要求涉及到本次操作流程的所有数据源都执行预提交(preCommit)操作,是或否

    • 第二阶段 : 根据第一阶段的返回结果来判断当前所有数据源是全部提交或回滚

    • 优点 : 从流程上我们可以看出来,XA保证数据的强一致性,要么一起提交,要么一起回滚

    • 缺点

      • 数据库必须提供对XA的支持,例如MySQL 5.7以下的版本

      • 受协议约束,事务资源(数据库连接)的锁定周期较长,而且因为事务资源的管理器是数据库本身,我们无法在应用层做处理,所以就导致了我们很难优化其性能

  • TCC(侵入业务)

    • try : 预留业务资源,对当前系统做校验,检查当前系统是否准备好提交,与XA的preCommit类似

    • confirm : 如果try执行成功,则确认执行提交操作

    • cancel : 如果try执行失败,则回滚事务,并释放预留资源

    • 优点 : 给了开发人员更大的施展空间,方便拓展,与XA一样,在强一致性上保持的不错

    • 缺点 : 对业务的侵入性很大,每一个分布式的分支都需要实现try,confirm,cancel操作,如果一旦需要改造,那绝对很让人愤怒

  • 基于消息中间件的最终一致性解决方案(最常见的方式,侵入业务)

    我们先介绍第一种,不支持事务的消息队列,例如RabbitMQ

    • 方案包括一个生产者,一个消费者,以及一个校验中心和一张存储消息的表

    • 生产者 : 在开启本地事务时保存一条数据到消息表中,状态置位未消费,事务提交之后将消息发送到队列中

    • 消费者 : 通过订阅队列获取到消息,在消费成功后(事务提交之后)修改消息表中的状态为已消费

    • 校验中心 : 校验中心也可以有俩种实现方式

        1. 定时扫描消息表,俩种结果,发送失败的,发送失败则重新发送成功但是消费者一直消费失败,设定消费失败次数,到达次数后标记该信息,调用生产者再次发送该消息((重新消费次数可设置)),重新计时,再次失败发送预警信息给开发人员,请求人工干预
        2. 创建一个校验中心队列,生产者在发送消息时同时发送一条到校验中心队列中,校验中心会有一个消费者来进行订阅该队列,拿到消息后将消息表中的状态置为处理中,然后定时扫描消息表,如果发现消息在规定时间内没有被消费成功,则再次将消息发送到消费者订阅的队列中,请求重新消费(重新消费次数可设置),重新计时,再次失败则请求人工干预
      • 以上的俩种方式原理都是一样的,但是第二种需要写更多的代码

    • 优点 : 将分布式事务进行拆分成本地事务,避免了分布式事务,实现了最终一致性

    • 缺点 : 需要些大量的代码,如果涉及的不够灵活,可能会给后续的开发和拓展带来不好的体验

    第二种,支持事务的消息队列,例如RocketMQ

    • 在开启本地事务之前先发送一条预处理消息(Prepared,很像MyBatis的防注入的处理方式),然后处理本地事务,处理结束后发送确认消息,此时会有俩种结果

      1. 确认消息发送成功,RocketMQ执行提交或回滚策略,生产者方面的流程结束

      2. 确认消息发送失败,RocketMQ内部会定时扫描直接本地事务消息表,查询到Prepared消息,如果当前Prepared消息没有被确认,则会向生产者确认,RocketMQ执行提交或回滚策略

    • 消费者订阅队列消费消息,RocketMQ会一直监测消息是否被成功消费,如果失败则一直重试,达到指定次数后申请人工干预

    不难发现,其实支持事务的消息队列也好,不支持的也好,解决分布式的方式很类似,都是将分布式事务分为俩个本地事务来处理

  • GTS / Seata (原FESCAR)(阿里开源的分布式事务解决方案,无业务侵入)

    在介绍FESCAR之前我们先来介绍一下FESCAR与XA

    从上图架构层次我们可以看到,XA的RM(事务管理器)在数据库层面上,而FESCAR的RM则是在应用层上面,这样就不会再依赖数据库对协议的依赖,开发人员可以最大限度的使用自己最擅长的

    2019年1月9日,阿里正式在github开源了基于GTS的FESCAR,在此之前我们一直使用的是自研、网上的的分布式处理方案,都没有一个权威的组织来进行后期维护,但是分布式事务又一直是微服务系统的一个不容忽视的问题,接下来我们来看一下阿里开源的分布式FESCAR

    FESCAR开源地址

案例 : 系统中有三个服务,分别是订单、账户、库存,它们都使用的是自己的数据源,在此基础上,本地事务可以保证数据的一致性

微服务架构发生了变化。提到的3个模块设计为3个不同数据源之上的3个服务(每个服务单独的数据库)。 本地事务能够保证每个服务中的数据一致性。

但是在分布式微服务系统总共,整个业务逻辑的一致性如何保证呢?

FESCAR怎么做?

FESCAR只是上述问题的解决方案。

首先,如何定义分布式事务

我们说,分布式事务是一个全局事务,由一批分支事务组成,通常分支事务只是本地事务

FESCAR有3个基本组件:

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

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

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

FESCAR管理分布式事务的典型生命周期:

  1. TM要求TC开始新的全球交易。TC生成表示全局事务的XID。

  2. XID通过微服务的调用链传播。

  3. RM将本地事务注册为XID到TC的相应全局事务的分支。

  4. TM要求TC提交或回滚XID的相应全局事务。

  5. TC在XID的相应全局事务下驱动所有分支事务以完成分支提交或回滚。

历史

  • TXC:淘宝交易构造函数。阿里巴巴中间件团队自2014年起启动该项目,以解决因应用程序架构从单片机改为微服务而导致的分布式事务问题。

  • GTS:全球交易服务。TXC作为Aliyun中间件产品,新名称GTS自2016年起发布。

  • FESCAR:我们从2019年开始基于TXC / GTS开源开源项目FESCAR,以便将来与社区密切合作。

FESCAR快速开始链接

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐