TX-LCN分布式事务之LCN模式
什么是LCN模式
LCN模式是TX-LCN分布式事务模式的一种,L-lock-锁定事务单元、C-confirm-确认事务模块状态、
notify-通知事务单元
原理
LCN模式是通过Spring AOP的方式代理Connection的方式实现对本地事务的操作,然后在由TxManager统一协调控制事务。
当本地事务提交回滚或者关闭连接时将会执行假操作,该代理的连接将由LCN连接池管理。
模式特点
该模式对代码的嵌入性为低。 该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。 该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。 该模式缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间。
源码解读
首先我们来看几个关键的类DataSourceAspect-数据源切面类、TransactionAspect事务切面类、LcnConnectionProxylcn
连接代理类、DTXLogicWeaver分布式事务调度器、DTXServiceExecutor分布式事务执行器
DataSourceAspect的作用
源码
@Aspect
@Component
public class DataSourceAspect implements Ordered {
private static final Logger log = LoggerFactory.getLogger(DataSourceAspect.class);
private final TxClientConfig txClientConfig;
private final DTXResourceWeaver dtxResourceWeaver;
public DataSourceAspect(TxClientConfig txClientConfig, DTXResourceWeaver dtxResourceWeaver) {
this.txClientConfig = txClientConfig;
this.dtxResourceWeaver = dtxResourceWeaver;
}
@Around("execution(* javax.sql.DataSource.getConnection(..))")
public Object around(ProceedingJoinPoint point) throws Throwable {
return this.dtxResourceWeaver.getConnection(() -> {
return (Connection)point.proceed();
});
}
public int getOrder() {
return this.txClientConfig.getResourceOrder();
}
}
由该类的源码,我们能够知道,
lcn模式主要对数据库的连接进行了拦截代理。获取到数据库的连接交由lcn
来进行代理。
TransactionAspect 作用
源码
@Aspect
@Component
public class TransactionAspect implements Ordered {
private static final Logger log = LoggerFactory.getLogger(TransactionAspect.class);
private final TxClientConfig txClientConfig;
private final DTXLogicWeaver dtxLogicWeaver;
public TransactionAspect(TxClientConfig txClientConfig, DTXLogicWeaver dtxLogicWeaver) {
this.txClientConfig = txClientConfig;
this.dtxLogicWeaver = dtxLogicWeaver;
}
@Pointcut("@annotation(com.codingapi.txlcn.tc.annotation.LcnTransaction)")
public void lcnTransactionPointcut() {
}
@Around("lcnTransactionPointcut() && !txcTransactionPointcut()&& !tccTransactionPointcut() && !txTransactionPointcut()")
public Object runWithLcnTransaction(ProceedingJoinPoint point) throws Throwable {
DTXInfo dtxInfo = DTXInfo.getFromCache(point);
LcnTransaction lcnTransaction = (LcnTransaction)dtxInfo.getBusinessMethod().getAnnotation(LcnTransaction.class);
dtxInfo.setTransactionType("lcn");
dtxInfo.setTransactionPropagation(lcnTransaction.propagation());
DTXLogicWeaver var10000 = this.dtxLogicWeaver;
point.getClass();
return var10000.runTransaction(dtxInfo, point::proceed);
}
public int getOrder() {
return this.txClientConfig.getDtxAspectOrder();
}
}
由该类的源码,我们能够明白,通过解析
@LcnTransaction注解进行相应的操作。代码会调用到DTXLogicWeaver类
DTXLogicWeaver 作用
public Object runTransaction(DTXInfo dtxInfo, BusinessCallback business) throws Throwable {
if (Objects.isNull(DTXLocalContext.cur())) {
DTXLocalContext.getOrNew();
log.debug("<---- TxLcn start ---->");
DTXLocalContext dtxLocalContext = DTXLocalContext.getOrNew();
TxContext txContext;
if (this.globalContext.hasTxContext()) {
txContext = this.globalContext.txContext();
dtxLocalContext.setInGroup(true);
log.debug("Unit[{}] used parent's TxContext[{}].", dtxInfo.getUnitId(), txContext.getGroupId());
} else {
txContext = this.globalContext.startTx();
}
if (Objects.nonNull(dtxLocalContext.getGroupId())) {
dtxLocalContext.setDestroy(false);
}
dtxLocalContext.setUnitId(dtxInfo.getUnitId());
dtxLocalContext.setGroupId(txContext.getGroupId());
dtxLocalContext.setTransactionType(dtxInfo.getTransactionType());
TxTransactionInfo info = new TxTransactionInfo();
info.setBusinessCallback(business);
info.setGroupId(txContext.getGroupId());
info.setUnitId(dtxInfo.getUnitId());
info.setPointMethod(dtxInfo.getBusinessMethod());
info.setPropagation(dtxInfo.getTransactionPropagation());
info.setTransactionInfo(dtxInfo.getTransactionInfo());
info.setTransactionType(dtxInfo.getTransactionType());
info.setTransactionStart(txContext.isDtxStart());
boolean var15 = false;
Object var6;
try {
var15 = true;
var6 = this.transactionServiceExecutor.transactionRunning(info);
var15 = false;
} finally {
if (var15) {
if (dtxLocalContext.isDestroy()) {
synchronized(txContext.getLock()) {
txContext.getLock().notifyAll();
}
if (!dtxLocalContext.isInGroup()) {
this.globalContext.destroyTx();
}
DTXLocalContext.makeNeverAppeared();
TracingContext.tracing().destroy();
}
log.debug("<---- TxLcn end ---->");
}
}
if (dtxLocalContext.isDestroy()) {
synchronized(txContext.getLock()) {
txContext.getLock().notifyAll();
}
if (!dtxLocalContext.isInGroup()) {
this.globalContext.destroyTx();
}
DTXLocalContext.makeNeverAppeared();
TracingContext.tracing().destroy();
}
log.debug("<---- TxLcn end ---->");
return var6;
} else {
return business.call();
}
}
以上代码是该类的核心逻辑,可以看出来
TX-LCN事务的处理全部都是走的这个类的该方法,最终会调用到DTXServiceExecutor分布式事务执行器
DTXServiceExecutor 作用
/**
* 事务业务执行
*
* @param info info
* @return Object
* @throws Throwable Throwable
*/
public Object transactionRunning(TxTransactionInfo info) throws Throwable {
// 1. 获取事务类型
String transactionType = info.getTransactionType();
// 2. 获取事务传播状态
DTXPropagationState propagationState = propagationResolver.resolvePropagationState(info);
// 2.1 如果不参与分布式事务立即终止
if (propagationState.isIgnored()) {
return info.getBusinessCallback().call();
}
// 3. 获取本地分布式事务控制器
DTXLocalControl dtxLocalControl = txLcnBeanHelper.loadDTXLocalControl(transactionType, propagationState);
// 4. 织入事务操作
try {
// 4.1 记录事务类型到事务上下文
Set<String> transactionTypeSet = globalContext.txContext(info.getGroupId()).getTransactionTypes();
transactionTypeSet.add(transactionType);
dtxLocalControl.preBusinessCode(info);
// 4.2 业务执行前
txLogger.txTrace(
info.getGroupId(), info.getUnitId(), "pre business code, unit type: {}", transactionType);
// 4.3 执行业务
Object result = dtxLocalControl.doBusinessCode(info);
// 4.4 业务执行成功
txLogger.txTrace(info.getGroupId(), info.getUnitId(), "business success");
dtxLocalControl.onBusinessCodeSuccess(info, result);
return result;
} catch (TransactionException e) {
txLogger.error(info.getGroupId(), info.getUnitId(), "before business code error");
throw e;
} catch (Throwable e) {
// 4.5 业务执行失败
txLogger.error(info.getGroupId(), info.getUnitId(), Transactions.TAG_TRANSACTION,
"business code error");
dtxLocalControl.onBusinessCodeError(info, e);
throw e;
} finally {
// 4.6 业务执行完毕
dtxLocalControl.postBusinessCode(info);
}
}
通过以上代码可以看出,该类是整个事务执行关键类。
以上就是LCN模式比较核心的代码,其他的分支代码就不一一赘述了
实战
由上一篇分布式事务之TX-LCN 我们规划了俩个TC分别是lcn-order
服务和lcn-pay服务,我们的思路是订单服务调用支付服务,分别在订单服务表t_order和支付服务表t_pay中插入插入数据。
订单服务核心代码和数据表脚本
代码
/**
* @author:triumphxx
* @Date:2021/10/24
* @Time:2:13 下午
* @微信公众号:北漂码农有话说
* @网站:http://blog.triumphxx.com.cn
* @GitHub https://github.com/triumphxx
* @Desc:
**/
@RestController
public class LcnOrderController {
@Autowired
TOrderDao tOrderDao;
@Autowired
private RestTemplate restTemplate;
@PostMapping("/add-order")
@Transactional(rollbackFor = Exception.class)
@LcnTransaction
public String add(){
TOrder bean = new TOrder();
bean.setTId(1);
bean.setTName("order");
restTemplate.postForEntity("http://lcn-pay/add-pay","",String.class);
// int i = 1/0;
tOrderDao.insert(bean);
return "新增订单成功";
}
}
脚本
CREATE TABLE `t_order` (
`t_id` int(11) NOT NULL,
`t_name` varchar(45) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
支付服务核心代码和数据表脚本
代码
/**
* @author:triumphxx
* @Date:2021/10/24
* @Time:2:26 下午
* @微信公众号:北漂码农有话说
* @网站:http://blog.triumphxx.com.cn
* @GitHub https://github.com/triumphxx
* @Desc:
**/
@RestController
public class LcnPayController {
@Autowired
TPayDao tPayDao;
@PostMapping("/add-pay")
@Transactional(rollbackFor = Exception.class)
@LcnTransaction
public String addPay(){
TPay tPay = new TPay();
tPay.setTId(1);
tPay.setTName("t_pay");
int i = tPayDao.insertSelective(tPay);
return "新增支付成功";
}
}
脚本
CREATE TABLE `t_pay` (
`t_id` int(11) NOT NULL,
`t_name` varchar(45) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
测试流程
启动 Redis启动 TM启动注册中心 eureka-server启动服务 lcn-order启动服务 lcn-pay请求接口 http://localhost:8001/add-order代码创造异常看数据是否进行回滚
小结
本篇我们分析了TX-LCN分布式事务的lcn模式的原理及相关源码,以及搭建服务的进行测试。希望能对大家有所帮助。
源码地址源码传送门

TX-LCN分布式事务之LCN模式的更多相关文章
- LCN分布式事务管理(一)
前言 好久没写东西了,9月份换了份工作,一上来就忙的要死.根本没时间学东西,好在新公司的新项目里面遇到了之前没遇到过的难题.那遇到难题就要想办法解决咯,一个请求,调用两个服务,同时操作更新两个数据库. ...
- 基于Dubbo的分布式事务框架(LCN)
原文地址:http://原文地址:https://github.com/1991wangliang/transaction 基于Dubbo的分布式事务框架(LCN) 该框架依赖Redis/dubbo/ ...
- Dubbo学习系列之十五(Seata分布式事务方案TCC模式)
上篇的续集. 工具: Idea201902/JDK11/Gradle5.6.2/Mysql8.0.11/Lombok0.27/Postman7.5.0/SpringBoot2.1.9/Nacos1.1 ...
- Dubbo学习系列之十四(Seata分布式事务方案AT模式)
一直说写有关最新技术的文章,但前面似乎都有点偏了,只能说算主流技术,今天这个主题,我觉得应该名副其实.分布式微服务的深水区并不是单个微服务的设计,而是服务间的数据一致性问题!解决了这个问题,才算是把分 ...
- 分布式事务 Seata Saga 模式首秀以及三种模式详解 | Meetup#3 回顾
https://mp.weixin.qq.com/s/67NvEVljnU-0-6rb7MWpGw 分布式事务 Seata Saga 模式首秀以及三种模式详解 | Meetup#3 回顾 原创 蚂蚁金 ...
- 通过Dapr实现一个简单的基于.net的微服务电商系统(十九)——分布式事务之Saga模式
在之前的系列文章中聊过分布式事务的一种实现方案,即通过在集群中暴露actor服务来实现分布式事务的本地原子化.但是actor服务本身有其特殊性,场景上并不通用.所以今天来讲讲分布式事务实现方案之sag ...
- 微服务分布式事务之LCN、TCC
在亿级流量架构之分布式事务解决方案对比中, 已经简单阐明了从本机事务到分布式事务的演变过程, 文章的最后简单说明了TCC事务, 这儿将会深入了解TCC事务是原理, 以及理论支持, 最后会用Demo举例 ...
- 分布式事务(Seata) 四大模式详解
前言 在上一节中我们讲解了,关于分布式事务和seata的基本介绍和使用,感兴趣的小伙伴可以回顾一下<别再说你不知道分布式事务了!> 最后小农也说了,下期会带给大家关于Seata中关于sea ...
- lcn 分布式事务协调者集群原理
lcn集群原理图: 1.实现原理: 1.1 LCN获取tm协调者注册地址 1. LCN客户端项目启动的时候,首先会注册到事务协调者中去,然后读取配置文件tm事务协调者的注册地址. 2. 向该事务协调者 ...
随机推荐
- 史上最详细的信号使用说明(已被收藏和N次)
Unix环境高级编程(第三版) 第10章 信号 文章目录 1. 引言 2. 信号的概念 2.1 信号操作之忽略信号 2.2 信号操作之捕捉信号 2.3 信号操作之执行系统默认操作 2.4 常见的信号 ...
- JDBC基础和使用
内存泄漏意思就是内存越来越少了,因为垃圾太多: 线程泄露就是线程池中线程越来越少,执行过程中异常,没有返回给线程池,线程池中线程越来越少: 一.概念 二.快速入门 三.JDBC各个类详解 1.driv ...
- 从线上日志统计接口访问量QPS
这一阵子在面试,连续遇到好几家(大小厂都有)问我的项目线上qps的情况了,说实话,我作为一个大头兵,本来没关注过这个数据,只能含混地给个"大概.也许"的回答. 回来之后,我决定对业 ...
- go新建一个工程
使用go mod 可以在任何地方新建工程 工程目录 main.go //引用子包必须格式"工程目录/子包" go.mod 子包 编译工程: go build
- leetcode-H2O生成(多线程)
现在有两种线程,氧 oxygen 和氢 hydrogen,你的目标是组织这两种线程来产生水分子. 存在一个屏障(barrier)使得每个线程必须等候直到一个完整水分子能够被产生出来. 氢和氧线程会被分 ...
- Django学习day09随堂笔记
每日测验 """ 今日考题: 1.choices参数的应用场景有哪些,如何获取该字段的值 2.django是什么模型的框架,简述MTV与MVC模型 3.多对多表关系有几种 ...
- mysql更新数据时:当想mysql某插入有某字段设置了unique且和之前相同时,会报错,并停止运行
这个在mysql5.7会报错: 如openid设为unique: 1062 - Duplicate entry 'oTfYq6PKne00IrcTqphmKqKnsahM' for key 'qx_w ...
- 解决IE浏览器 点击子元素重复调用执行 mouseover 与mouseout兼容性问题
将函数配对换为下面2个就可以解决兼容性问题. mouseenter() mouseleave(0
- AVS 端能力之音频播放模块
功能简介 音频播放 音频流播放 URL文件播放 播放控制 播放 暂停 继续 停止 其它功能(AVS服务器端实现) 支持播放列表 支持上一首下一首切换 支持电台 事件指令集 AudioPlayer 端能 ...
- YbtOJ#903-染色方案【拉格朗日插值,NTT,分治】
正题 题目链接:https://www.ybtoj.com.cn/contest/115/problem/3 题目大意 两个长度为\(n+1\)的序列\(a,b\) \(a_i\)表示涂了\(i\)个 ...