Spring Cloud之Seata的学习

news2024/11/29 10:51:04

目录

案例准备

分布式事务

基本理论

CAP定理

BASE理论

Seata

部署TC服务

数据库准备

修改Nacos配置并导入信息

启动Seata

集成Seata

XA模式原理

Seata的XA实现

优点

缺点

实现

AT模式原理

AT模式的脏写问题

Seata的AT实现

XA与AT的区别

TCC模式原理

空回滚与业务悬挂问题

Seata的TCC实现

Saga模式原理

四种模式对比

高可用


案例资料下载地址:day02分布式事务

案例准备

  1. 将资料中的seata-demo.sql文件加载到数据库中。
  2. seata-demo文件夹使用IDEA打开。
  3. 启动nacos与所有微服务
  4. 测试订单,发送POST请求

观察数据库

成功添加一个订单。

分布式事务

在分布式系统下,一个业务跨越多个服务或数据源,每个服务都是一个分支事务。要保证所有的分支事务最终状态保持一致,这样的事务就是分布式事务。

基本理论

CAP定理

所谓CAP是指:

  • Consistency(一致性)
  • Availability(可用性)
  • Partition tolerance(分区容错性)

分布式系统无法同时满足这三个指标。当分区出现时系统的一致性和可用性无法同时满足。

一致性:用户访问分布式系统中的任意节点,得到的数据必须一致

可用性:用户访问集群中任意健康节点,必须得到相应,而不是超时或拒绝

分区容错:所谓分区,就是因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接,形成独立分区。而容错,就是在集群出现分区时,整个系统也要持续对外提供服务

BASE理论

BASE理论是对CAP的一种解决思路

  • Basically Available(基本可用):分布式系统在出现故障时,允许损失部分可用性,即保证核心可用
  • Soft state(软状态):在一定时间内,允许出现中间状态,比如临时的不一致状态
  • Eventually Consistent(最终一致性):虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。

而分布式事务最大的问题是各个子事务的一致性问题,因此可以借鉴CAP定理和BASE理论:

  • AP模式:各子事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致。
  • CP模式:各个子事务执行后互相等待,同时提交,同时回滚,达成强一致。但事务等待过程中,处于弱可用状态。

比如ES集群就属于CP,当ES集群一个节点网络故障时,会被剔除,该节点的数据分片会被保存在其他节点上。保证了高一致性,低可用性。所以是CP。

Seata

Seata事务管理中存在三个角色:

  • TC(Transaction Coordinator)事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚
  • TM(Transaction Manager)事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务
  • RM(Resource Manager)资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

Seata提供了四种不同的分布式事务解决方案:

  • XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入
  • TCC模式:最终一致的分阶段事务模式,有业务侵入
  • AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式
  • SAGA模式:长事务模式,有业务侵入

部署TC服务

下载Seata:GitHub - seata/seata: :fire: Seata is an easy-to-use, high-performance, open source distributed transaction solution.

需要注意的是,最新版本与1.5.0之前的Seata配置方式不同,这里我采用的是1.7.1版本。资料中的版本也有对应的文本文件。

数据库准备

sql文件保存在该目录下\script\server\db。在数据库中执行sql文件。

修改Nacos配置并导入信息

修改文件\seata\conf\application.yml

在Nacos创建配置文件

将路径\script\config-center\config.txt修改如下内容后全选粘贴到nacos中的配置内容处

启动Seata

双击启动bin目录下的seata-server.bat

访问地址http://localhost:7091/#/login

默认用户名与密码都为seata

nacos控制台可以看到seata的节点

集成Seata

引入依赖

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.seata</groupId>
                    <artifactId>seata-spring-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <version>1.4.2</version>
        </dependency>

配置文件添加如下内容

seata:
  registry:
    # nacos配置
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: DEFAULT_GROUP
      namespace: seata-demo
      username: nacos
      password: nacos
  tx-service-group: seata-demo #事务组名称
  #如果seata是一个集群,那么在nacos寻找seata节点时,就要这么去配置
  service:
    vgroup-mapping: #事务组与cluster的映射关系
      seata-demo: SH

seata客户端发现seata集群中的节点,需要tx-service-group中的值作为key,vgroup-mapping作为value的映射关系去寻找。

XA模式原理

是强一致性的事务。基于数据库本身特性实现的

Seata的XA实现

RM一阶段的工作:

  • 注册分支事务到TC
  • 执行分支业务sql但不提交
  • 报告执行状态到TC

TC二阶段的工作:

  • TC检测各分支事务执行状态
    • 如果都成功,通知所有RM提交事务
    • 如果有失败,通知所有RM回滚事务

RM二阶段的工作:

  • 接收TC指令,提交或回滚事务
优点
  • 强一致性
  • 基于数据库做了一层封装,实现简单
缺点
  • 如果关联的事务较多,且耗时较长,已经执行完毕的事务还需要等待其他事务完成,耗费资源更大,性能差。
  • 依赖关系数据库实现,如果使用Redis则不适用
实现

修改配置文件。该配置作用是对数据源做代理,拦截所有sql请求,RM帮我们调用数据库的XA接口。

seata:
  data-source-proxy-mode: XA

给全局事务的入口方法添加注解@GlobalTransactional注解(和Transactional注解添加位置一样)

@GlobalTransactional
public Long create(Order order) {
	// 创建订单
	orderMapper.insert(order);
	try {
    	// 扣用户余额
    	accountClient.deduct(order.getUserId(), order.getMoney());
    	// 扣库存
    	storageClient.deduct(order.getCommodityCode(), order.getCount());

	} catch (FeignException e) {
    	log.error("下单失败,原因:{}", e.contentUTF8(), e);
    	throw new RuntimeException(e.contentUTF8(), e);
	}
	return order.getId();
}

接下来测试一个一定失败的新增订单请求

查看数据库发现,库存没有减少,用户金额也没扣减,订单也没增加。观察控制台输出,是扣款之后再回滚

AT模式原理

AT模式同样是分阶段提交事务模型。不过弥补了XA模型中资源锁定周期过长的缺陷。

阶段一RM工作:

  • 注册分支事务
  • 记录undo-log(快照)
  • 执行sql并提交
  • 报告事务状态

阶段二提交时RM的工作:删除undo-log

阶段二回滚时RM的工作:根据undo-log恢复数据到更新前

AT模式的脏写问题

出现脏写的情况是因为事务没有做到隔离,为了解决这个问题,引入了全局锁概念。TC记录当前正在操作某行数据的事务,该事务持有全局锁,具有执行权。主要记录的是事务id、事务操作的表名、以及该表的被修改的数据主键值。

需要注意的是。这里存在两个锁,一个是数据库的DB锁一个是Seata管理的全局锁。为了避免死锁问题,通常全局锁在等待300毫秒内还没有获取到锁就进行超时回滚。比DB锁超时时间要短很多。

由于全局锁只会对被Seata管理的事务生效,对普通事务不生效。因此,可能存在普通事务修改数据的可能,导致Seata管理的事务进行回滚时造成的脏写事件。对此,AT模式不光会保存修改前的数据快照(before-image),也会保存修改后的数据快照(after-image)。当进行回滚时,会对数据库当前的数据与修改后的数据库快照(after-image)进行对比。如果发现不一样,则说明在回滚之前期间有其他事务修改了数据。

Seata的AT实现

将资料中的seata-at.sql文件以文本方式打开。里面有两张表的创建语句。将lock_table表(管理全局锁的表)创建语句在TC服务的数据库中执行。undo_log表(快照存放表,由RM管理)在微服务访问的数据库中创建。

修改配置文件中的事务模式为AT

seata:
  data-source-proxy-mode: AT

重启服务后,发送一次库存不足的请求观察是否扣减余额和创建订单。

可以看到,执行了扣款操作,但是根据快照回滚并将快照信息删除。

XA与AT的区别

XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源

XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚

XA模式强一致;AT模式最终一致

TCC模式原理

TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法:

  • Try:资源的检测和预留
  • Confirm:完成资源操作业务;要求Try 成功 Confirm 一定要能成功。
  • Cancel:预留资源释放,可以理解为try的反向操作。

阶段一进行资源预留。阶段二不管是提交还是回滚,都是对自己预留部分的操作。不需要像XA模式加锁或是AT模式一样保存数据快照。在性能上比前两种模式更好一些。

TCC模式的优点:

  • 一阶段完成直接提交事务,释放数据库资源,性能好
  • 相比AT模型,无需生成快照,无需使用全局锁,性能最强
  • 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库

TCC模式的缺点:

  • 有代码侵入,需要人为编写try、Confirm和Cancel接口,麻烦
  • 软状态,事务是最终一致
  • 需要考虑Confirm和Cancel的失败情况,做好幂等处理

空回滚与业务悬挂问题

当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚。

而对于已经空回滚的业务,如果后续阻塞的事务恢复,继续执行try,就永远不可能confire或cancel,这就是业务悬挂。应当阻止执行空回滚后的try操作,避免悬挂。

Seata的TCC实现

TCC实现资源冻结通常是新加一张表,被冻结的资源放在该表中,根据该表信息来执行Cancel

Try业务:

  • 记录冻结金额和事务状态到account_freeze表
  • 扣减account表可用金额

Confirm业务:根据xid删除account_freeze表的冻结记录

Cancel业务:

  • 修改account_freeze表冻结金额为0,state为2
  • 修改account表,恢复可用金额

如何判断是否空回滚:cancel业务中,根据xid查询account_freeze,如果为null则说明try还没做,需要空回滚。

如何避免业务悬挂:try业务中,根据xid查询account_freeze ,如果已经存在则证明Cancel已经执行,拒绝执行try业务

将资料中的account_freeze_tbl.sql文件在微服务访问的数据库执行。

编写Java业务代码

@LocalTCC
public interface AccountTCCService {
    //该注解在哪个方法上就说明哪个方法为try方法,name值要和方法名一样
    @TwoPhaseBusinessAction(name="deduct",commitMethod = "confirm",rollbackMethod = "cancel")
    void deduct(@BusinessActionContextParameter(paramName = "userId") String userId,//参数中的注解会被加载到BusinessActionContext中。
                @BusinessActionContextParameter(paramName = "money") int money);

    boolean confirm(BusinessActionContext ctx);

    boolean cancel(BusinessActionContext ctx);
}
@Service
public class AccountTCCServiceImpl implements AccountTCCService {
    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    private AccountFreezeMapper accountFreezeMapper;

    @Override
    public void deduct(String userId, int money) {
        //获取全局事务ID
        String xid = RootContext.getXID();
        AccountFreeze accountFreeze = accountFreezeMapper.selectById(xid);
        if (accountFreeze!=null){
            //已经执行过Cancel,不去执行
            return;
        }
        //扣减用户余额
        accountMapper.deduct(userId, money);
        //冻结表新增余额
        accountFreeze = new AccountFreeze();
        accountFreeze.setXid(xid);
        accountFreeze.setUserId(userId);
        accountFreeze.setFreezeMoney(money);
        accountFreeze.setState(AccountFreeze.State.TRY);
        accountFreezeMapper.insert(accountFreeze);
    }

    @Override
    public boolean confirm(BusinessActionContext ctx) {
        //提交事务,删除该表全局事务对应的数据就可以
        String xid = ctx.getXid();
        int count = accountFreezeMapper.deleteById(xid);
        return count == 1;
    }

    @Override
    public boolean cancel(BusinessActionContext ctx) {
        //判断是否为空回滚
        String xid = ctx.getXid();
        String userId = ctx.getActionContext("userId").toString();
        int money = (int) ctx.getActionContext("money");
        AccountFreeze accountFreeze = accountFreezeMapper.selectById(xid);
        if (accountFreeze == null) {
            //是空回滚
            accountFreeze = new AccountFreeze();
            accountFreeze.setXid(xid);
            accountFreeze.setUserId(userId);
            accountFreeze.setFreezeMoney(money);
            accountFreeze.setState(AccountFreeze.State.CANCEL);
            accountFreezeMapper.insert(accountFreeze);
            return true;
        }
        // 幂等处理
        if (accountFreeze.getState() == AccountFreeze.State.CANCEL) {
            return true;
        }
        //说明不是空回滚,恢复数据
        accountMapper.refund(userId, money);
        accountFreeze.setState(AccountFreeze.State.CANCEL);
        accountFreeze.setFreezeMoney(0);
        int count = accountFreezeMapper.updateById(accountFreeze);
        return count == 1;
    }
}

修改Controller代码,装配TCCService的bean对象。重启服务,再次发送一次库存不足请求

Saga模式原理

Saga模式是Seata提供的长事务解决方案。也分为两个阶段

一阶段:直接提交本地事务

二阶段:成功什么也不用做,失败通过编写补偿业务进行回滚

由于TCC是通过预留资源实现业务提交或回滚,而Saga是直接对资源本身进行操作,因此不存在事务隔离性,有一定安全问题。

优点:

  • 事务参与者可以基于事件驱动实现异步调用,吞吐高
  • 一阶段直接提交事务,无锁,性能好
  • 不同编写TCC中的三个阶段

缺点:

  • 软状态持续时间不确定,时效性差
  • 没有锁,没有事务隔离,会存在脏写问题

通常不适用该模式,通常使用AT模式,使用TCC或XA做补充

四种模式对比

XA

AT

TCC

SAGA

一致性

强一致

弱一致

弱一致

最终一致

隔离性

完全隔离

基于全局锁隔离

基于资源预留隔离

无隔离

代码入侵

要编写三个接口

要编写状态机和补偿业务

性能

很好

很好

场景

对一致性、隔离性有高要求的业务

基于关系型数据库的大多数分布式场景都可以

对性能要求较高的事务。有非关系型数据库要参与的事务

业务流程长,业务流程多。参与者包含其他公司或遗留系统服务,无法提供TCC模式要求的三个接口

高可用

TC服务作为Seata的核心服务,一定要保证高可用和异地容灾。接下来我们对Seata进行集群配置

将原来seata文件复制一份作为第二个节点

修改第二份文件集群名称。

接着启动2号节点

seata-server.bat -p 8092

接下来,我们需要将tx-service-group与cluster的映射关系都配置到nacos配置中心,方便生产环境下实现热更新部署。

微服务读取nacos配置文件

seata:
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      group: SEATA_GROUP
      data-id: client.properties

启动微服务观察

所有服务都注册到SH集群节点上。

修改nacos中的配置信息

实现了动态切换集群

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1172106.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习之基于Python+OpenCV+dlib的考生信息人脸识别系统(GUI界面)

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 深度学习在人脸识别领域的应用已经取得了显著的进展。Python是一种常用的编程语言&#xff0c;它提供了许多强大的库…

Python基础入门例程37-NP37 不低于与不超过(运算符)

最近的博文&#xff1a; Python基础入门例程36-NP36 谁的数字大&#xff08;运算符&#xff09;-CSDN博客 Python基础入门例程35-NP35 朋友的年龄是否相等&#xff08;运算符&#xff09;-CSDN博客 Python基础入门例程34-NP34 除法与取模运算&#xff08;运算符&#xff09;…

SpringSecurity全家桶 (二) ——实现原理

1. SpringSecurity的强大之处 当我们并未设置登录页面时&#xff0c;我们只需要导入SpringSecurity的依赖就可以令我们的界面进入保护状态&#xff0c;由下面例子可以凸显出&#xff1a; 随便写个接口 RequestMapping("/hello")public String hello(){return "H…

spring-boot中实现分片上传文件

一、上传文件基本实现 1、前端效果图展示&#xff0c;这里使用element-ui plus来展示样式效果 2、基础代码如下 <template><div><el-uploadref"uploadRef"class"upload-demo":limit"1":on-change"handleExceed":auto-…

PPT制作指南

诸神缄默不语-个人CSDN博文目录 文章目录 1. SOP2. PPT的目标3. PPT素材4. 内容框架5. 设计细节本文撰写过程中使用到的参考资料 1. SOP 分析目标→收集素材→明确框架→视觉呈现 2. PPT的目标 演讲型PPT&#xff1a;字少图多 阅读型PPT&#xff1a;需要文字解释 分析维度&…

shell学习脚本05(小滴课堂)

可以对海量的数据进行提取。 -v对提取的内容进行取反。 -n显示出行号。 -w精确匹配&#xff1a; -i:忽略大小写&#xff1a; -E正则匹配&#xff1a; cut命令&#xff1a; -d指定分隔符&#xff0c;-f指定截取区域&#xff1a; 截取第一列到第三列&#xff1a; 截取第二列到最…

一文带你了解如何让自动化测试框架更自动化

一、引言 ​对于大厂的同学来说&#xff0c;接口自动化是个老生常谈的话题了&#xff0c;毕竟每年的MTSC大会议题都已经能佐证了&#xff0c;不是大数据测试&#xff0c;就是AI测试等等&#xff08;越来越高大上了&#xff09;。不可否认这些专项的方向是质量智能化发展的方向…

第五章 Python文件操作

系列文章目录 第一章 Python 基础知识 第二章 python 字符串处理 第三章 python 数据类型 第四章 python 运算符与流程控制 第五章 python 文件操作 第六章 python 函数 第七章 python 常用内建函数 第八章 python 类(面向对象编程) 第九章 python 异常处理 第十章 python 自定…

SpringBoot框架使用AOP + 自定义注解实现请求日志记录

一、SpringBoot记录日志 文章目录 一、SpringBoot记录日志1.1、环境搭建1.2、配置FastJson1.3、自定义LogRecord注解1.4、定义日志实体类1.5、创建HttpRequestUtil工具类1.6、定义AOP切面1.7、编写测试类1.8、运行测试 1.1、环境搭建 搭建SpringBoot工程。引入【spring-boot-st…

Docker:Dockerfile语法

Docker&#xff1a;Dockerfile语法 1. 镜像2. 镜像结构3. Dockerfile 1. 镜像 前面我们一直在使用别人准备好的镜像&#xff0c;那如果我要部署一个Java项目&#xff0c;把它打包为一个镜像该怎么做呢&#xff1f; 2. 镜像结构 要想自己构建镜像&#xff0c;必须先了解镜像的…

【漏洞复现】IIS_7.o7.5解析漏洞

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描3、漏洞验证 1.5、修复建议 1.1、漏洞描述 漏洞原理&#xff1a; cgi.fix_path1 1.png/.php该…

送你几款开源IDC资产管理系统

更多运维技术&#xff0c;请关注微信公众号“运维之美” 送你几款开源IDC资产管理系统 1.phpIPAM2.NetBox3.IPPlan4.GestiIP5.RackTables 对于公司机房运维人员来说&#xff0c;你的idc资产管理清单可能还记录在各种excel表格中&#xff0c;当设备和ip变动的时候进行手动更新&a…

Java继承:抽取相同共性,实现代码复用

&#x1f451;专栏内容&#xff1a;Java⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、继承的概念二、继承的语法三、父类成员访问1、子类中访问父类成员变量Ⅰ、子类和父类不存在同名成员变量Ⅱ、子类和父类成员…

泄漏检测与修复(LDAR)过程管控平台(销售出租)VOCs便携式总烃分析仪(销售出租)

LDAR是Leak Detection and Repair&#xff08;泄漏检测与修复&#xff09;的缩写&#xff0c;也是国际上较先进的化工废气检测技术。LDAR主要通过检测化工企业原料输送管道、泵、阀门、法兰等易产生易产生挥发性有机物&#xff08;简称VOCs&#xff09;泄漏的部位&#xff0c;并…

turn.js 模版简单使用

turn.js 不修改添加原功能仅 替换、修改图片格式使用模版 HTML文件 turn.js官网&#xff1a;http://www.turnjs.com/# 第一步 1.点击链接去到官网 2.点击下载按钮 下载左侧示例压缩包 3.解压完成拿到示例文件 turnjs4 4.在samples目录下案例中查看意向使用的模版样式 …

OpenGL_Learn05(纹理)

1. 纹理贴图 wall.jpg (512512) (learnopengl-cn.github.io) 纹理过滤分为&#xff1a;邻近和线性&#xff0c;这跟opencv图像处理一样。 多级渐远纹理 四种采样方式&#xff1a; 代码实现&#xff1a; std_image.h https://github.com/nothings/stb/blob/master/stb_image.…

【数据结构】冒泡排序 (码源实现)

冒泡排序 前言一、冒泡排序运行图例二、算法实现基本思路三、算法实现步骤四、算法码源详解五、冒泡排序效率分析&#xff08;一&#xff09;时间复杂度——O&#xff08;N^2&#xff09;&#xff08;二&#xff09;空间复杂度——O&#xff08;1&#xff09;&#xff08;三&am…

【PC电脑windows-学习样例tusb_serial_device-ESP32的USB模拟串口程序+VScode建立工程+usb组件添加+-基础样例学习】

【PC电脑windows-学习样例tusb_serial_device-ESP32的USB模拟串口程序-基础样例学习】 1、概述2、实验环境3-1、 物品说明3-2、所遇问题&#xff1a;ESP32 cannot open source file "tinyusb.h"或者“tinyusb.h:No such file or directory ....”3-3、解决问题&#…

【多线程】静态代理

当使用静态代理模式时&#xff0c;我们会有一个真实的对象&#xff08;RealSubject&#xff09;&#xff0c;一个代理对象&#xff08;ProxySubject&#xff09;&#xff0c;代理对象将请求转发给真实对象&#xff0c;并可以在请求前后执行额外的操作。 真实对象和代理对象要实…