概述
随着单体项目越来越庞大,单体架构的缺陷日益明显。越多越多的公司都从传统的单体应用模式向新型的分布式应用模式转变。在分布式应用带来巨大优势的同时,也伴随着各种挑战。例如,系统容错、网络延迟和分布式事务等。
分布式事务介绍
事务是指由一组操作组成的一个工作单元,这个工作单元具有原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。
分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
CAP理论:一个分布式系统不可能同时满足一致性,可用性和分区容错性这个三个基本需求,最多只能同时满足其中两项
BASE理论:BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的缩写。是对CAP中一致性和可用性权限的结果,是基于CAP定理演化而来的,核心思想是即使无法做到强一致性,但每个应用都可以根据自身的业务特定,采用适当的方式来使系统达到最终一致性
分布式事务解决方案
1、二阶段提交(2PC)
二阶段提交协议是将事务的提交过程分成提交事务请求和执行事务提交两个阶段进行处理。
第一阶段:提交事务请求
事务询问:协调者向所有的参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参与者的响应
执行事务:各参与者节点执行事务操作,并将Undo和Redo信息记入事务日志中
如果参与者成功执事务操作,就反馈给协调者Yes响应,表示事物可以执行,如果没有成功 执行事务,就反馈给协调者No响应,表示事务不可以执行
二阶段提交一些的阶段一夜被称为投票阶段,即各参与者投票票表明是否可以继续执行接下去的事务提交操作
第二阶段:执行事务提交
假如协调者从所有的参与者或得反馈都是Yes响应,那么就会执行事务提交。
发送提交请求:协调者向所有参与者节点发出Commit请求
事务提交:参与者接受到Commit请求后,会正式执行事务提交操作,并在完成提交之后放弃整个事务执行期间占用的事务资源
反馈事务提交结果:参与者在完成事物提交之后,向协调者发送ACK消息
完成事务:协调者接收到所有参与者反馈的ACK消息后,完成事务
2、3PC提交
第一阶段:CanCommit
协调者向参与者发送CanCommit请求。询问是否可以执行事务提交操作。然后开始等待参与者的响应。
参与者接到CanCommit请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返回Yes响应,并进入预备状态;否则反馈No。
第二阶段:PreCommit
协调者向所有参与者节点发出 preCommit 的请求,并进入 prepared 状态。
参与者受到 preCommit 请求后,会执行事务操作,对应 2PC 准备阶段中的 “执行事务”,也会 Undo 和 Redo 信息记录到事务日志中。
如果参与者成功执行了事务,就反馈 ACK 响应,同时等待指令:提交(commit) 或终止(abort)。
第三阶段:Do Commit
协调者接收到各参与者发送的ACK响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送 doCommit 请求。
参与者接收到 doCommit 请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。
事务提交完之后,向协调者发送 ACK 响应。
协调者接收到所有参与者的 ACK 响应之后,完成事务。
3、补偿事务(TCC)
TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:
Try 阶段主要是对业务系统做检测及资源预留
Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。
Seata分布式事务方案
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。本文主要介绍seata的at与tcc模式以及spring搭建
AT模式介绍
AT 模式是一种无侵入的分布式事务解决方案。在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。
首先我们拿到官网所展示的文档来更直观的描述
可以通过上图得出:
一阶段本地事务提交前,需要确保先拿到全局锁 。拿不到全局锁,不能提交本地事务。拿全局锁的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。
两个全局事务 tx1 和 tx2,分别对 a 表的 m 字段进行更新操作,m 的初始值 1000。tx1 先开始,开启本地事务,拿到本地锁,更新操作 m = 1000 - 100 = 900。本地事务提交前,先拿到该记录的全局锁 ,本地提交释放本地锁。tx2后开始,开启本地事务,拿到本地锁,更新操作 m = 900 - 100 = 800。本地事务提交前,尝试拿该记录的 全局锁 ,tx1 全局提交前,该记录的全局锁被 tx1 持有,tx2 需要重试等待 全局锁 ,如tx2等待所超时,那么tx2便回滚本地事务所以他不会产生脏数据。
TCC模式介绍
Seata 框架把每组 TCC 接口当做一个 Resource,称为 TCC Resource。这套 TCC 接口可以是 RPC,也以是服务内 JVM 调用。在业务启动时,Seata 框架会自动扫描识别到 TCC 接口的调用方和发布方。如果是 RPC 的话,就是 sofa:reference、sofa:service、dubbo:reference、dubbo:service 等。
扫描到 TCC 接口的调用方和发布方之后。如果是发布方,会在业务启动时向 TC 注册 TCC Resource,与 DataSource Resource 一样,每个资源也会带有一个资源 ID。
如果是调用方,Seata 框架会给调用方加上切面,与 AT 模式一样,在运行时,该切面会拦截所有对 TCC 接口的调用。每调用一次 Try 接口,切面会先向 TC 注册一个分支事务,然后才去执行原来的 RPC 调用。当请求链路调用完成后,TC 通过分支事务的资源 ID 回调到正确的参与者去执行对应 TCC 资源的 Confirm 或 Cancel 方法。
Springboot整合seata
1、启动seata-server
我们先从官网下载seata-server
解压seata-server安装包到指定目录,修改conf目录下registry.conf,配置注册中心与配置中心,配置如下图所示
修改conf目录下的file.conf配置文件,主要修改自定义事务组名称,事务日志存储模式为db及数据库连接信息;如下图
执行seata-server中/bin中的启动文件启动seata服务
2、Seata数据源代理配置
在启动类中取消数据源的自动创建
创建配置使用Seata对数据源进行代理:
3、AT模式
使用@GlobalTransactional注解开启分布式事务:
服务A
服务B
4、TCC模式
使用@GlobalTransactional注解开启分布式事务:
A、B服务需要定义TCC接口并实现,这里我们以A服务为例讲解
由于我们使用的是SpringCloud+Feign,Feign的调用基于http,因此此处我们使用LocalTCC便可。值得注意的是,@LocalTCC一定需要注解在接口上,此接口可以是寻常的业务接口,只要实现了TCC的两阶段提交对应方法便可。
@LocalTCC 适用于SpringCloud+Feign模式下的TCC
@TwoPhaseBusinessAction 注解try方法,其中name为当前tcc方法的bean名称,写方法名便可(记得全局唯一),commitMethod指向提交方法,rollbackMethod指向事务回滚方法。指定好三个方法之后,seata会根据事务的成功或失败,通过动态代理去帮我们自动调用提交或者回滚。
@BusinessActionContextParameter 注解可以将参数传递到二阶段(commitMethod/rollbackMethod)的方法。
BusinessActionContext 便是指TCC事务上下文
prepare实现方法如下
commit实现方法如下(防悬挂控制与幂等控制)
Rollback实现如下(需要实现允许空回滚)