当前位置: 首页 > 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.dtcms.com/a/317419.html

相关文章:

  • Java 排序教程
  • P1983 [NOIP 2013 普及组] 车站分级
  • 《第五篇》基于RapidOCR的图片和PDF文档加载器实现详解
  • 分布式文件系统07-小文件系统的请求异步化高并发性能优化
  • LeetCode——118. 杨辉三角
  • 数据结构(四)内核链表、栈与队列
  • Go语言数据类型深度解析:位、字节与进制
  • 实时数据可视化工具SciChart.js v4.0即将发布——扩展更多极坐标图表
  • 【前端】问题总结
  • Spring Data MongoDB 教程:用 @Query 快速实现字段查询
  • 大前端游戏应用中 AI 角色行为智能控制
  • STM32CubeIDE新建项目过程记录备忘(九) A/D转换并用串口定时上报
  • 基于可视化分析的房地产市场监测与预警机制,展示二手房的价格趋势、区域分布、户型结构等关键信息
  • DataKit 采集器敏感信息加密最佳实践
  • NineData 新增支持 AWS ElastiCache 复制链路
  • 从 0 到 1 创建 InfluxDB 3 表:标签、字段、命名规范一篇讲透
  • 什么是单元测试?
  • 完美解决hive external表中csv字段内容含“,“逗号的问题
  • 贪心算法学习 跳跃游戏
  • 利用OJ判题的多语言优雅解耦方法深入体会模板方法模式、策略模式、工厂模式的妙用
  • macOS Python 安装
  • 《设计模式之禅》笔记摘录 - 13.迭代器模式
  • 外观模式(Facade Pattern)及其应用场景
  • 【设计模式精解】从根上理解模板方法设计模式及其应用
  • RN项目环境搭建和使用-Mac版本(模拟器启动不起来的排查)
  • Python虚拟环境完全指南:pyenv vs venv 在macOS上的使用详解
  • Mac安装WebStorm
  • java中Reflection反射(一)
  • MCU AI/ML - 弥合智能和嵌入式系统之间的差距
  • Java猜数字简易小游戏可复制