Java详细分析LCN框架分布式事务
作者:kaico2018 发布时间:2022-10-17 15:49:08
2PC两阶段提交协议
分布式事务通常采用2PC协议,全称Two Phase Commitment Protocol。该协议主要为了解决在分布式数据库场景下,所有节点间数据一致性的问题。分布式事务通过2PC协议将提交分成两个阶段:
阶段一为准备(prepare)阶段。即所有的参与者准备执行事务并锁住需要的资源。参与者ready时,向transaction manager报告已准备就绪。
阶段二为提交阶段(commit)。当transaction manager确认所有参与者都ready后,向所有参与者发送commit命令。
2PC和3PC的区别就是解决参与者超时的问题和多加了一层询问,保证数据的传输可靠性。
LCN
LCN并不生产事务,LCN只是本地事务的协调工,TX-LCN定位于一款事务协调性框架,框架其本身并不操作事务,而是基于对事务的协调从而达到事务一致性的效果。
参考文档:https://www.codingapi.com/docs/txlcn-preface/
LCN基本实现原理
发起方与参与方都与我们的 LCN 管理器一直保持长连接;
发起方在调用接口之前,先向 LCN 管理器申请一个全局的事务分组id;
发起方调用接口的时候在请求头中传递事务分组id;
参与方获取到请求头中有事务分组的id的,则当前业务逻辑执行完实现假关闭,不会提交或者回滚当前的事务。
发起方调用完接口后,如果出现异常的情况下,在通知给事务协调者回滚事务,这时候事务协调则告诉给参与方回滚当前的事务。
搭建全局协调者
1、在github上面下载 Lcn 源代码
仓库地址:https://github.com/codingapi/tx-lcn,注意下载版本。
2、将项目导入idea中,启动对应的项目
修改对应的配置文件:
spring.application.name=TransactionManager
server.port=7970
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://www.kaicostudy.com:3306/transaction_lcn?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=123456mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.use-generated-keys=true# TxManager Host Ip
tx-lcn.manager.host=127.0.0.1
# TxClient连接请求端口
tx-lcn.manager.port=8070
# 心跳检测时间(ms)
tx-lcn.manager.heart-time=15000
# 分布式事务执行总时间
tx-lcn.manager.dtx-time=30000
#参数延迟删除时间单位ms
tx-lcn.message.netty.attr-delay-time=10000
tx-lcn.manager.concurrent-level=128
# 开启日志
tx-lcn.logger.enabled=true
logging.level.com.codingapi=debug#redis 连接信息
spring.redis.host=www.kaicostudy.com
spring.redis.port=6379
redis\u5BC6\u7801
#spring.redis.password=
将项目中提供的SQL语句在数据库中执行,创建对应的表。
请求路径:http://127.0.0.1:7970/admin/index.html
默认登录密码:codingapi
登录成功页面
端口介绍:
8070:TM事务消息端口
7970:后台管理页面登录页面
使用LCN解决分布式事务问题
在分布式系统A系统调用B系统服务接口的时候时候,两个服务的都需要使用 LCN 来控制分布式事务。
使用步骤:
1、引入lcn 相关的maven依赖
<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、yml 配置文件增加 lcn 的配置
tx-lcn:
client:
manager-address: www.kaicostudy.com:8070
logger:
enabled: true
3、使用
springboot项目主类上加上注解:@EnableDistributedTransaction
参与方与发起方都要加上该注解
@LcnTransaction
@Transactional
调用案例:
A服务的方法,需要去调用B服务方法
@Service
public class ServiceA {
@Autowired
private ValueDao valueDao; //本地db操作
@Autowired
private ServiceB serviceB;//远程B模块业务
@LcnTransaction //分布式事务注解
@Transactional //本地事务注解
public String execute(String value) throws BusinessException {
// step1. call remote service B
String result = serviceB.rpc(value); // (1)
// step2. local store operate. DTX commit if save success, rollback if not.
valueDao.save(value); // (2)
valueDao.saveBackup(value); // (3)
return result + " > " + "ok-A";
}
}
B服务的方法,被A服务的方法调用
@Service
public class ServiceB {
@Autowired
private ValueDao valueDao; //本地db操作
@LcnTransaction //分布式事务注解
@Transactional //本地事务注解
public String rpc(String value) throws BusinessException {
valueDao.save(value); // (4)
valueDao.saveBackup(value); // (5)
return "ok-B";
}
}
源码分析
一个请求一个线程
代码执行逻辑:
1、判断方法是否有加上@L cnTransaction, 如果有加上该注解则直接会走 切面类 TransactionAspect
2、判断当前线程缓存中是否有事务分组id,如果没有缓存则是为发起方,如果有缓存则是为参与方
3、随机的创建分组的id,将该分组id注册到协调者中。
4、本地 threadLock 缓存该事务分组id
5、A服务(发起方)调用B(参与方)服务的接口,重写了 RequestInterceptor(该接口是feign框架提供的 * ,基本上每个rpc框架都会提供类似的 * ) feign 客户端,将该事务分组id设置到请求中
6、执行到B服务接口,Spring TracingApplier实现,在请求之前拦截,从请求头中获取事务分组id,放入到当前线程缓存中
7、B服务接口走到aop里面代码时,会先判断是发起方还是参与方。
8、从缓存中获取该事务分组id,当前派单服务则是为参与方,在告诉给协调者加入该事务分组。.
Lcn 如何判断自己是发起方还是参与方?
根据当前的线程threadlocal 中获取事务分组id, 如果能够成功获取到则是为参与方,没有能够获取到就是为发起方。
参与方如何加入LCN全局协调者?
发起方会把事务id注册到协调者里面去,参与方根据请求头里面的事务分组id加入该事务。
发起方如何通知全局回滚还是提交?
发起方的方法执行完成之后,会修改事务状态,再根据全局协调者通知其他参与者事务执行完成。反之,如果发起方的方法执行方法异常,事务状态改为错误状态,再通过全局协调者发送给其他参与者,参与者再回滚事务即可。
A调用B,B调用C 到底会生产几次事务id?
每次原远程调用接口都会生成一个事务id,但是一条调用链上只有一个事务分组id(全局id)。只有A是发起方,B和C都是参与方。可以从请求头中获取到事务分组id就是参与方,表示加入到这个分组里面去的。
入口:@LcnTransaction,TransactionAspect 切面类。
feign 重写的 * ,给请求头添加信息,事务分组id
来源:https://blog.csdn.net/weixin_44044929/article/details/124820214
猜你喜欢
- 接触Spring快半年了,前段时间刚用Spring4+S2H4做完了自己的毕设,但是很明显感觉对Spring尤其是IOC容器的实现原理理解的
- 本文实例为大家分享了android绘制曲线和折线图的具体代码,供大家参考,具体内容如下(曲线) (折线)1.CurveView.j
- @Profiles和@PropertySource根据环境切换配置文件使用@PropertySource注解加载配置文件,并制定解析配置文件
- 本文实例为大家分享了Android匹配关键字标红的具体代码,供大家参考,具体内容如下1. 单关键字匹配若只需匹配 搜索内容 可以
- 用来练手还是不错的,分享大家看一下,还是一些新颖点的!哈哈 就是自定义DataGridView,方便每个功能部分调用!简单!再次重
- 背景在实际开发过程中,会遇到需要编写各类打印模板模板的需求,当然这些在WPF开发中更为常见,但是使用XAML写编辑的打印模板又不能直接发送给
- 一、List<T>对象中的T是值类型的情况(int 类型等)对于值类型的List直接用以下方法就可以复制:List<T&g
- 本文实例讲述了asp.net实现遍历Request的信息操作。分享给大家供大家参考,具体如下:#需求:在服务端获取从客户端发送过来的所有数据
- WPF 实现筛选下拉多选控件框架使用.NET4 至 .NET6;Visual Studio 2022;创建 MultiSelect
- 布局布局效果如下,下面每个“网格”都是一个按钮,点击按钮,就会有相应的事件发生。由于UniformG
- 在Monkeyrunner做自动化测试时,可以使用模拟器,当然也可以选择用真机。不过,要想通过电脑来安装软件,操作手机,则必须先安装手机驱动
- global 是 C# 2.0 中新增的关键字,理论上说,如果代码写得好的话,根本不需要用到它。假设你现在写了一个类,名字叫 System。
- RibbonRibbon 是 Netflix开源的基于HTTP和TCP等协议负载均衡组件Ribbon 可以用来做客户端负载均衡,调用注册中心
- 前言 随着Java生态愈发庞大,各种各样的新技术层出不穷,这也给大家的学习带来了很多困惑,这么多技术我该学什么,盲目的在各种新技术间
- 在业务开发过程中我们会遇到形形色色的注解,但是框架自有的注解并不是总能满足复杂的业务需求,我们可以自定义注解来满足我们的需求。根据注解使用的
- 本文实例讲述了C#编程实现统计文件夹内文件和隐藏文件的方法。分享给大家供大家参考,具体如下:C#统计文件夹内的文件,包括隐藏文件,显示那个隐
- 创建一个用户类类型的集合,手动输入用户库主要是判定输入的用户名和密码是否与库中的匹配做好区别是用户名输入错误还是密码输入错误的提示。定义用户
- 在平时做系统项目时,经常会需要做导出功能,不论是导出excel,还是导出cvs文件。我下面的demo是在springmvc的框架下实现的。1
- 实现思路收集路径点集。平均采样路径点集。将路径点集转为 LineB。把 LineB 数据传给 Pat
- 判断参数是否为空并作为查询条件@Override public Page<DemandEntity>