利用netty模拟分布式事务锁

1.分布式事务简介
a,所谓分布式事务,是指的涉及多个数据库操作的事务,小编在开发中曾遇到过很多分布式事务的场景,今天我自己模拟了一个解决分布式事务的demo,希望和大家交流,并指出优缺点
b,下面简单画一个图说明一下分布式事务的小例子
利用netty模拟分布式事务锁_第1张图片
c,图中简单描述了一个下订单的场景,用户下单首先会调用订单系统中下单,同时减库存,可能还有其他许多操作,如果下单顺利则不议论,如果下单失败,那么库存也应该失败 否则会产生不一致,那么如何解决这种情况呢,是保持一致呢?就是我们所说的分布式事务
2.准备工作
a,首先我们些个demo演示一下分布式事务的场景。
利用netty模拟分布式事务锁_第2张图片

我们用springboot创建两个demo (demo1,demo2) demo1 demo2只做一件事,分别往数据库表中插入一条数据,以此来模拟 下单和减库存,
利用netty模拟分布式事务锁_第3张图片

利用netty模拟分布式事务锁_第4张图片

在demo1当中去调用demo2模块中的减库存方法,同时在两个模块中开启事务,加入spring自带的@TransActional注解 ,大家注意,在demo1当中,我们调用完demo2以后,为了方便区分 我们将demo1插入的数据用"server1"表示,将demo2的插入操作用"server2"表示,系统会抛异常 (int i = 100/0),那么会发生什么呢?接下来我们分别启动两个项目 ,为了测试出效果,首先我们先查询一下数据库
利用netty模拟分布式事务锁_第5张图片

然后我们访问demo1
利用netty模拟分布式事务锁_第6张图片

我们会发现浏览器抛出了异常,此时demo1抛出了异常,由于都开启了事务,demo1会回滚,可是demo2是在抛异常之前已被调用,正常执行了,此时我们查询一下数据库:
利用netty模拟分布式事务锁_第7张图片

但是这种结果并不是我们想要的,我们想要的是demo1执行失败以后,同时告知demo2也进行同样的回滚操作
首先我们会思考一个问题,如何使多系统之间保持同步呢,我们知道 无论是 下单 还是减少库存,首先都需要先获取数据库连接,开启事务,但是 他们之间没有必然联系,怎么办呢?在事务开始和结束时,我们应当有一个对事务的控制权,同时我们还应该有一个事务管理器的角色,(这个事务管理器记录着事务的ID, 状态,以及事务组的概念)
3.进入正题
a.我们可以先自定义一个注解 来表示我们的分布式事务,为什么要写一个注解呢(因为小编吹在此一个小小的牛)spring就是这么干的,我们也来这么干一下。
利用netty模拟分布式事务锁_第8张图片

b.然后,再利用springAop定义一个环绕通知,因为前面小编说过,事务是基于连接的,通过调用jdk获取的连接commit() 和rollback()方法并不满足我们的需求 ,因此我们要把切点定在调用jdk获取数据库连接上。改写这两个方法 。
利用netty模拟分布式事务锁_第9张图片
利用netty模拟分布式事务锁_第10张图片
c.再次定义一个切向我们自定义注解的环绕通知,这样做的目的是为了在执行改注解上的方法的时候会加入环绕通知,在通知里面获得事务的控制权
利用netty模拟分布式事务锁_第11张图片利用netty模拟分布式事务锁_第12张图片

d.写到这里,我们先来探讨一下,我们认为事物的概念,我们封装一个具有groupId,transactionId,trnasActionType和一个线程可重入锁等属性的WlTransAction对象
利用netty模拟分布式事务锁_第13张图片
利用netty模拟分布式事务锁_第14张图片
利用netty模拟分布式事务锁_第15张图片
利用netty模拟分布式事务锁_第16张图片
e.接下来,我们已经写好了自己的事务对象,前面说到了中间要有一个事务协调者(事务管理器)来穿插于我们的两个系统当中,在这里,由于小编认为一次事务是在一个线程生命周期内完成的(单线程),所以这里我们用ThreadLocal来存放当前事务,组ID,以及事务数量,用它们来组成我们的事务管理器,并且这里使用netty来作为我们事务管理器运行来做消息通知
首先需要依赖netty的jar包,如果不知道的童鞋,可以从 maven官网搜索netty pom依赖即可
利用netty模拟分布式事务锁_第17张图片
f.jar包依赖好以后,我们对netty进行封装一下, 因为我们要基于netty做一个简易的事务管理器,首先,netty是基于端口和主机之间的通信,只要服务端和客户端是在一个物理网段内的主机都可以进行通信
由于小编这里只涉及两个项目和一台主机,所以只需要利用一个端口,我们以8080端口作为服务端,客户端只需向8080端口发送 或从8080端口接收信号(封装的事务数据)从而执行我们的切面逻辑即可,既然是服务端,我们只需创建一个事务管理器的java模块即可 同时封装一个用于封装事务属性和动作的类,用于向客户端发送消息和从客户端接收消息
利用netty模拟分布式事务锁_第18张图片
利用netty模拟分布式事务锁_第19张图片
利用netty模拟分布式事务锁_第20张图片
利用netty模拟分布式事务锁_第21张图片
此类封装了一个Server,用于创建channel和管道
利用netty模拟分布式事务锁_第22张图片
接下来是main主程序,在主程序里面 我们设置服务端端口为8080,并且启动
利用netty模拟分布式事务锁_第23张图片
服务端创建好以后,我们来写netty的客户端,因为客户端需要从事务管理器中去获取当前的事务信息,并向服务端推送消息.
利用netty模拟分布式事务锁_第24张图片
利用netty模拟分布式事务锁_第25张图片
这里小编用到了spring的InitializingBean(钩子)这个接口,是为了spring在初始化该类时可以执行该方法
利用netty模拟分布式事务锁_第26张图片
利用netty模拟分布式事务锁_第27张图片
客户端,服务器端写好以后,我们开始来完善上面留下的切面逻辑
利用netty模拟分布式事务锁_第28张图片
利用netty模拟分布式事务锁_第29张图片
利用netty模拟分布式事务锁_第30张图片
辅助工作完了以后,我们开始向需要开启分布式事务的方法上添加注解
利用netty模拟分布式事务锁_第31张图片
利用netty模拟分布式事务锁_第32张图片
最后,启动demo1,demo2,以及事务管理器
利用netty模拟分布式事务锁_第33张图片
利用netty模拟分布式事务锁_第34张图片
利用netty模拟分布式事务锁_第35张图片
浏览器访问demo1
利用netty模拟分布式事务锁_第36张图片
利用netty模拟分布式事务锁_第37张图片
利用netty模拟分布式事务锁_第38张图片
我们可以看到,由于demo1有一段异常代码,会抛出异常,由于我们加了自定义分布式事务注解,那么demo2的数据库操作也应该回滚,我们查询一下数据库
利用netty模拟分布式事务锁_第39张图片
此时都回滚了 达到了我们的效果,那么我们修改demo1当中的代码,使之能够顺利执行,并重启,并重新访问demo1
利用netty模拟分布式事务锁_第40张图片

利用netty模拟分布式事务锁_第41张图片
此时,demo1和demo2由于没有异常代码,会顺利执行,数据库应该同时提交,会添加两条数据,我们查询一下数据库
利用netty模拟分布式事务锁_第42张图片
此时我们看到,都顺利的提交了,这就是小编模拟的分布式事务框架,代码中有什么不足指出 欢迎大家讨论和学习。
项目构建完成,正确输出。至此,spring5.0.x项目构建编译完成。大功告
希望对大家有所帮助,谢谢!
作者:安静的小海豹
来源:CSDN
原文:https://blog.csdn.net/qq_38412637/article/details/85255625
版权声明:本文为博主原创文章,转载请附上博文链接!
版权声明:本文为博主原创文章,转载请附上博文链接!

作者:qq_38412637
来源:CSDN
原文:https://blog.csdn.net/qq_38412637/article/details/85316378
版权声明:本文为博主原创文章,转载请附上博文链接!

你可能感兴趣的:(事务运用)