1. LCN架构介绍
注:本节内容来自LCN官网。
LCN由两大模块组成,TC和TM,TC作为模块的依赖框架,提供TX-LCN的标准支持,TM作为分布式事务的控制方。事务发起方或者参与方都由TC端来控制。
核心步骤
-
创建事务组
是指在事务发起方开始执行业务代码之前先调用TxManager创建事务组对象,然后拿到事务标示GroupId的过程。
-
加入事务组
添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息通知给TxManager的操作。
-
通知事务组
是指在发起方执行完业务代码以后,将发起方执行结果状态通知给TxManager,TxManager将根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方。
在LCN源码中,有2个关键的切面类:
- TransactionAspect:是一个切面类,对@LcnTransaction注解设置了环绕通知
- DataSourceAspect:是一个切面类,对javax.sql.DataSource.getConnection设置环绕通知,即在本地事务获取连接时被拦截。
2. TC逻辑梳理
当我们某个方法需要分布式事务时,就在该方法上加上@LcnTransaction注解,整个流程的开始就在这里。
TransactionAspect类是一个切面类,对@LcnTransaction注解设置了环绕通知,在碰到注解@LcnTransaction开始了整个流程。
DTXServiceExecutor类的transactionRunning方法,在该方法中,定义好了程序的执行步骤,只是根据事务类型和传播状态从spring容器中获取对应的控制器,再调用控制器的相关方法执行具体逻辑。一个典型的策略模式的应用。(看源码之前我还以为是用模板模式实现呢)
2.1 分布式事务控制器DTXLocalControl介绍
2.1.1 事务发起者重点方法介绍(LcnStartingTransaction)
- preBusinessCode方法:向TM创建事务组,并设置本地事务需要事务代理
- onBusinessCodeError方法:在本地缓存设置分布式事务状态为0,表示异常情况
- onBusinessCodeSuccess方法:在本地缓存设置分布式事务状态为1,表示正常情况
- postBusinessCode方法:向TM发送事务状态最终通知,并回滚本地事务,因此TM通知其实是通知不到事务发起者的。
2.1.2 事务参与者重点方法介绍(LcnRunningTransaction)
- preBusinessCode方法:设置本地事务需要事务代理
- onBusinessCodeError方法:回滚本地事务
- onBusinessCodeSuccess方法:向TM发送加入事务组消息,并表示本地事务是执行成功了的
- postBusinessCode方法:空方法
2.2 事务发起者创建事务组
2.3 事务发起者向TM发送分布式事务状态消息
注意:
- 只有分布式事务发起者才能向TM发起最终通知,各个参与者的postBusinessCode方法为空。
- 如果远程子事务出现业务异常,但被统一处理了,并最终向事务发起者返回统一的格式化数据(一般对外提供的服务都会有这个封装),则需要在事务发起者进行结果解析,根据解析的结果决定是否抛出异常。
最佳实践:
- 在FeignClient中(事务发起者端),新增拦截器,在拦截器中对返回结果进行解析,如果是错误,则直接抛出异常。
2.4 事务参与者加入事务组
注意:
- 只有当事务参与者执行本地事务成功后,才会向TM发送加入事务组的消息,因此加入事务组是在onBusinessCodeSuccess方法中完成的。
2.5 根据TM的通知最终提交或回滚本地事务
3. 本地事务的提交和回滚
@LcnTransaction //分布式事务注解
@Transactional //本地事务注解
public String saveUser(UserVo userVo){
OrderVo order=new OrderVo();
order.setId(userVo.getId());
order.setUserName(userVo.getUserName());
order.setOthers(userVo.getOthers());
orderService.saveOrder(order);
UserVo user=new UserVo();
user.setId(userVo.getId());
user.setUserName(userVo.getUserName());
user.setOthers(userVo.getOthers());
userMapper.saveUser(user);
double x=10/0;
return "ok-B" + " > " + "ok-A";
}
在上述代码的执行流程如下:
- 执行TransactionAspect相关方法,向TM创建事务组
- 被@Transactional拦截,开启本地事务
- DataSourceAspect创建本地事务连接代理类LcnConnectionProxy
- 执行saveUser方法,获取本地事务连接,会被DataSourceAspect拦截
- saveUser方法执行完后执行事务提交和回滚,即执行LcnConnectionProxy的commit或rollback方法
- 当TC获取到TM的提交或回滚通知时,会调用本地事务代理的notify方法
- LcnConnectionProxy执行被代理类的commit或rollback方法,并真正的将数据刷新到数据库中。
4. 总结
- 创建自定义注解LcnTransaction,及其注解拦截类TransactionAspect
- 在TransactionAspect类中实现向TM发送新增或加入事务组逻辑
- 创建DataSourceAspect拦截本地事务
- 创建本地事务连接代理类LcnConnectionProxy,代理本地事务,执行commit和rollback
- 在接收到TM发送提交或回滚通知时,调用本地事务连接代理类LcnConnectionProxy的notify方法,执行真实的事务提交或回滚操作。