当前位置: 首页 > news >正文

分布式事务Seata、LCN的原理深度剖析

一、基本概念和方法

1.1、分布式事务产生背景

(1)如果是在传统项目中,使用同一个数据源,在数据用同一个事务管理器的情况下,不存在分布式事务问题,因为有事务的传播行为帮助我们实现。每个数据源都有自己独立的事务管理,每个数据源中的事务管理都互不影响。

(2)如果在单体项目中,存在多个不同的数据源,每个事务源都有自己独立的事务管理器,每个事务管理器互不影响,也会存在分布式事务的问题。

1.2、Rpc通讯中产生的分布式事务的问题原因

(1)调用放调用完rpc接口后,突然程序抛出异常,调用放的事务回滚了,但是被调用方接口没有回滚,则会产生数据不一致问题。在每个jvm中都有自己的事务,每个事务都互不影响。

(2)被调用方的接口失败的话,调用方可以根据返回的结果,手动回滚调用方本地事务。

1.3、分布式事务解决框架有哪些?

(1)传统多数据源的情况下,采用jta + atominc将每个独立的事务管理器统一交给我们的atominc全局事务管理。

(2)基于MQ保证数据一致性,最终一致性。

(3)基于Rocketmq解决分布式事务,核心采用自带事务消息。

(4)基于LCN模式解决分布式事务,tcc/2pc/lcn模式。全局事务id来回传递

(5)基于阿里巴巴seata解决分布式事务(背景非常强大)。

分布式事务处理的核心思想:tcc、2pc、最终一致性。

1.4、CAP和Base理论

这个定理的内容是指的是在一个分布式系统中、Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。

一致性(C):

        在分布式系统中的所有数据备份,在同一时刻是否同样的值,等同于所有节点访问同一份最新的数据副本。即:在分布式系统中,同一时刻所有的节点的数据都是相同的。

可用性(A):

        在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求,对数据更新具备高可用性。即:集群中部分节点出现了故障,集群的整体也能够给响应

分区容错性(P):

        以实际效果而言,分区相当于对通信的时限要求,系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。即:分区容错性是指系统能够容忍节点之间的网络通信的故障,意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。

CAP总结:

        三者无法兼顾,在分布式系统当中可以容忍网络之间出现的通讯故障,最终选择要么是CP或者AP。

CP:当你网络出现故障之后,只能保证数据一致性,但是不能保证可用性; 如zookeeper;

AP:当你网络出现故障之后,不能保证数据一致性,但是能够保证可用性;如eureka;

Base理论

        BASE是Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的缩写,即:是由基本可用、软状态、最终一致性三个组成,是对cap中一致性和可用性权衡的结果。

1.5、Seata与LCN的区别

1、基本实现的思路是一样的,唯一区别在于回滚的方式 2、LCN采用代理数据源假关闭连接,暂时不提交本地事务,但是容易造成数据的死锁。

Seata采用undo_log的形式逆向生成sql语句实现回滚,避免死锁现象但是容易出现脏读。

二、LCN解决分布式事务

2.1、基于LCN解决分布式事务原理

1、发起方和参与方项目启动的时候,必须和全局的协调者保持长连接。

2、发起方向我们的lcn的协调者申请一个事务分组id。

3、发起方调用参与方接口的时候,在请求头中将该事务的分组id传递给参与方。

4、参与方获取到请求头中有传递对应的事务分组id,当前业务执行完毕后,不会提交事务,基于数据源做的一个代理,采用jdbc假关闭,做了一行锁,容易锁行数据,

5、发起方如果产生回滚或提交,都会将该结果发送给协调者,然后协调者通知所有的参与方。

如果参与方一直没有等到协调者来告知的话,协调者端默认超时时间是5秒,超时之后就会做回滚操作。

LCN如何判断自己是发起方还是参与方?

        根据当前的线程threadlocal中获取事务分组id,如果能够成功获取到,则为事务的参与方,如果获取不到则为发起方。

2.2、LCN整个实现过程

(1)判断方法是否有加上@LcnTransaction注解,如果有,则直接会走TransactionAspect来做切面拦截。

(2)当前线程缓存中是否有事务分组id,如果没有缓存,则视为发起方,如果有缓存,则视为参与方。

(3)随机的创建分组的id,将该分组id注册到协调者中。

(4)本地threadlocal缓存该事务分组id;

(5)通过feign调用接口时,底层重写RequestInterceptorFeign客户端,从threadlocal中获取该事务分组id,并设置到请求中。

(6)参与方在aop中通过SpringTracingApplier实现,在请求之前拦截,从请求头中获取事务分组id,放入到缓存中。

(7)从缓存中获取该事务分组id,当前服务则为参与方,再告诉给协调者加入该事务分组。

2.3、SpringCloud整合LCN解决分布式事务问题

2.3.1、引入相关pom依赖:

<dependency><groupId>com.codingapi.txlcn</groupId><artifactId>txlcn-tc</artifactId><version>5.0.2.RELEASE</version>
</dependency><dependency><groupId>com.codingapi.txlcn</groupId><artifactId>txlcn-txmsg-netty</artifactId><version>5.0.2.RELEASE</version>
</dependency>

 2.3.2、调用方和参与方添加以下配置,并在启动类上添加@EnableDistributedTransaction注解。

tx-lcn: client: manager-address:127.0.0.1:8070logger: enabled: true

  2.3.3、在发起方和调用方的接口实现上都需添加@Transactional 和 @LcnTransaction注解即可。

三、Seata解决分布式事务原理及过程

3.1、Seata的组成部分

(1)事务协调器(TC):

                维护全局事务和分支事务的状态,驱动全局提交或事务回滚,类似LCN的协调者。

(2)事务管理器(TM):

                定义全局事务的范围;开始全局事务,提交或回滚全局事务,类似LCN的发起方。

(3)资源管理器(RM):

                管理分支事务正在处理的资源,与TC进行对话,来注册分支事务,并报告分支事务的状态,并驱动分支事务的提交和回滚,相当于LCN的参与方。

3.2、Seata解决分布式事务原理

(1)发起方(TM)和参与方(RM)再项目启动的时候,和协调者(TC)保持长连接。

(2)发起方调用接口之前向TC获取一个全局的事务id为xid,注册到Seata中,AOP中实现的。

(3)使用feign客户端调用接口的时候,seata重写了feign客户端,在请求头中传递该xid。

(4)参与方从请求头中获取该xid,方法执行完毕不会立马提交,而是等待协调者通知提交的状态。

3.3、Seata实现过程

  1. 发起方(TM)向协调者(TC)申请一个全局的事务xid,保存到ThreadLocal中;
  2. 发起方(TM)和参与方(RM)都会被Seata数据源代理,在原生的sql执行之前和之后,

把操作前和操作后的内容保存到undo_log表中(前置镜像和后置镜像),方便后期实现回滚。

  1. 发起方(TM)使用feign客户端调用接口的时候,在threadlocal中获取xid,设置到请求头中。
  2. 参与方(RM)从请求中头中获取到该全局事务xid,设置到threadlocal中,并且向协调者注册该分支。
  3. 如果发起方(TM)调用接口成功之后,如果报错的情况下则通知给协调者,协调者再告诉所有的分支都开始回滚,

直接根据本地事务id+xid查询undo_log表 ,逆向生成sql语句回滚,同时删除该undo_log日志。

  1. 如果发起方(TM)调用接口成功之后,如果没有报错的情况下则通知给协调者,协调者在告诉所有的分支都开始提交事务,直接根据本地事务id+xid删除对应的undo_log表记录即可。

缺陷:根据undolog表,逆向回滚,会产生脏读问题,在回滚或网络阻塞的情况下,会导致接口也超时和阻塞。

3.4、JAVA项目整合Seata解决分布式事务示例

3.4.1、服务端整合

(1)启动seata-server 全局事务协调者  seata-server.bat;

(2)修改 registry.conf和file.conf配置;

(3)在需要解决分布式事务的数据库中,手动创建undo_log表;

3.4.2、客户端整合(对应的方法上添加@GlobalTranscational注解)

(1)需要要将file.conf registry.conf 拷贝的项目中,默认的my_test_tx_group为分组的id、 全局的协调者连接地址。

<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-seata</artifactId><version>2.2.0.RELEASE</version>
</dependency>

(2)配置代理的数据源

@Configurationpublic class DataSourceProxyConfig {@Bean@ConfigurationProperties(prefix = spring.datasource)public DataSource dataSource() {return new DruidDataSource();}@Beanpublic DataSourceProxy dataSourceProxy(DataSourcedataSource) {return new DataSourceProxy(dataSource);}@Beanpublic SqlSessionFactorysqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {SqlSessionFactoryBeansqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSourceProxy);sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());return sqlSessionFactoryBean.getObject();}}

(3)修改application.yml的配置:

四、手写Seata解决分布式事务设计思想

发起方在方法上面加上@GlobalTransactional,会被aop拦截;

Aop的入口分析:

1、我们的项目中会引入到SpringCloud-Alibaba-seata.jar 执行spring.factories配置,读取GlobalTransactionAutoConfiguration配置类,会加载GlobalTransactionScanner全局事务扫描器到Spring的容器中。

2、GlobalTransactionScanner实现了AbstractAutoProxyCreator、InitializingBean,

a)AbstractAutoProxyCreator:是SpringAOP原生类,帮助我们创建代理对象;

b)InitializingBean SpringBean生命周期初始化;

GlobalTransactionScanner  对象初始化成功之后开始注册我们的tm和tc,

AbstractAutoProxyCreator  回调的方法 wrapIfNecessary  来创建我们的GlobalTransactionalInterceptor,

执行我们发起方方法前会执行我们的GlobalTransactionalInterceptor的invoke方法 ,然后再执行我们的transactionalTemplate的execute方法。

http://www.lryc.cn/news/612276.html

相关文章:

  • vue中reactive()和ref()的用法
  • selenium操作指南
  • 状态模式及优化
  • 【机器学习篇】02day.python机器学习篇Scikit-learn基础操作
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘gensim’问题
  • Session 和 JWT(JSON Web Token)
  • python:非常流行和重要的Python机器学习库scikit-learn 介绍
  • 毕业设计选题推荐之基于Spark的在线教育投融数据可视化分析系统 |爬虫|大数据|大屏|预测|深度学习|数据分析|数据挖掘
  • Packets Frames 数据包和帧
  • 大数据存储域——Hive数据仓库工具
  • 数据结构---二级指针(应用场景)、内核链表、栈(系统栈、实现方式)、队列(实现方式、应用)
  • STM32学习记录--Day8
  • 键帽(dp)
  • 【数字图像处理系列笔记】Ch03:图像的变换
  • Redis Redis 常见数据类型
  • 高等数学(工本)----00023 速记宝典
  • JAVA高级编程第八章
  • windows系统创建ubuntu系统
  • Python与自动化运维:构建智能IT基础设施的终极方案
  • 第七章课后综合练习
  • 学习日志29 python
  • 达梦数据库数据守护集群启动与关闭标准流程
  • 对接钉钉审批过程记录(C#版本)
  • 什么是逻辑外键?我们要怎么实现逻辑外键?
  • IDEA 2025下载安装教程【超详细】保姆级图文教程(附安装包)
  • 2 SpringBoot项目对接单点登录说明
  • 【0基础PS】PS工具详解--直接选择工具
  • capset系统调用及示例
  • 数据安全防护所需要的关键要素
  • 数据结构学习(days04)