分布式事务讲解 您所在的位置:网站首页 网词lcn什么意思 分布式事务讲解

分布式事务讲解

2024-07-12 05:52| 来源: 网络整理| 查看: 265

分布式事务讲解 - TX-LCN分布式事务框架(含LCN、TCC、TXC三种模式) 分布式事务系列博客:TX-LCN框架原理LCN原理及主要特点代码实现实现场景创建数据库及表(三个数据库,两个表)创建工程(四个)事务管理系统(lcn-tm)引依赖写配置加注解 Eureka注册及发现系统(eureka)引依赖写配置加注解 订单系统(lcn-order)引依赖写配置加注解启动类定义RestTemplate的bean(用于调用lcn-pay系统方法)添加测试Controller类(OrderController) 支付系统(lcn-pay)引依赖(与lcn-order系统依赖相同)写配置加注解添加测试Controller类(PayController) 执行测试测试事务执行成功测试事务执行失败 TCC原理及主要特点代码实现(以LCN测试代码为基础添加TCC代码)订单系统(lcn-order)新增测试Controller类(OrderTccController) 支付系统(lcn-pay)新增测试Controller类(PayTccController) 执行测试测试事务执行成功测试事务执行失败 TXC原理及主要特点 总结源码地址

分布式事务系列博客:

【分布式事务讲解 - 2PC、3PC】

今天所讲解的TX-LCN框架,里面集成了LCN、TCC、TXC三种事务模式,并且TX-LCN是中国开发的开源框架,在源码里面可以看到有中文注释,比较有亲切感,,我们分别来详细讲一下这三种模式,并且带有代码实现。

TX-LCN框架原理

框架主要由事务参与者(TxClient)、事务管理者(TxManager)两部分构成,事务控制的原理图如下: 在这里插入图片描述 由图中我们就能理解TX-LCN框架的原理了,第四步可以作为阶段分割线,第四步上面所有操作就像是2PC的第一阶段,第四步及下面所有操作就像是2PC的第二阶段,如果大家忘记了2PC的话,可以看一下我写的前一篇博客【分布式事务讲解 - 2PC、3PC】,这个时序图其实就是框架源码流程的翻译,源码中也用了很多巧妙的手法来实现整体事务的工作流程,这些我会在下面介绍旗下三种模式的时候给大家一起讲解。

LCN 原理及主要特点 LCN模式使用了代理技术,把本地事务的数据库连接维持住不释放,通过TxManager来统一控制事务。在本地事务中,对事务进行的commit/rollback/close操作都是假操作,大家再远那种可以看到代理后的数据库连接中的commit/rollback/close方法都是空方法,限制本地事务进行除执行SQL以外的其他操作。可以说在本地事务执行SQL之后就会一直保持数据库连接,直到TxManager发来提交或者回滚操作才会使本地事务释放数据库连接,这样增加了数据库连接的占用时长。最后一阶段的TxManager发出的提交或者回滚命令是通过Netty发送的,会循环所有TxClient并发送指令。 代码实现

不光是对LCN这个模式的实现,对于所有框架的实现无非主要就三个步骤:

引依赖写配置加注解

像我之前写的【Spring Cloud框架搭建系列】大家也能看得出来,下面我给大家展示一下。

实现场景

用户买东西下单,下完单之后保存【单据记录】以及【支付金额】,只要有一个操作执行失败,那就下单失败,否则,整体流程执行成功,下单成功。

创建数据库及表(三个数据库,两个表) -- 用户维护日志及异常信息,可按需创建表 CREATE DATABASE tx-manager; CREATE DATABASE lcn-pay; CREATE TABLE `tbl_pay` ( `id` int(11) NOT NULL AUTO_INCREMENT, `pay_name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; CREATE DATABASE lcn-order; CREATE TABLE `tbl_order` ( `id` int(11) NOT NULL AUTO_INCREMENT, `order_name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; 创建工程(四个) 事务管理系统(lcn-tm) 引依赖 com.codingapi.txlcn txlcn-tm 5.0.2.RELEASE com.codingapi.txlcn txlcn-tc 5.0.2.RELEASE com.codingapi.txlcn txlcn-txmsg-netty 5.0.2.RELEASE 写配置 # TM事务管理器的服务端WEB访问端口。提供一个可视化的界面。端口自定义。 server.port=3000 # TM事务管理器,需要访问数据库,实现分布式事务状态记录。 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/tx-manager?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=wk3515134 # 为spring应用起名。 spring.application.name=tx-lcn-transaction-manager # TM事务管理器,提供的WEB管理平台的登录密码。无用户名。 默认是codingapi tx-lcn.manager.admin-key=wk3515134 # 日志。如果需要TM记录日志。则开启,赋值为true,会自动为tx-manager数据库添加t_logger表。 tx-lcn.logger.enabled=true 加注解

在lcn-tm工程启动类添加注解

@EnableTransactionManagerServer Eureka注册及发现系统(eureka) 引依赖 org.springframework.cloud spring-cloud-starter-netflix-eureka-server 写配置 spring: application: name: cloud-eureka eureka: instance: # 在hosts文件中修改内容 hostname: euk-server-one.com client: register-with-eureka: false fetch-registry: false service-url: # 5 24 defaultZone: http://euk-server-one.com:8080/eureka/ 加注解

在eureka工程启动类添加注解

@EnableEurekaServer

如果配置后eureka启动失败,那就移步我的一篇博客【Spring Cloud完整组件搭建之路-Eureka】。

订单系统(lcn-order) 引依赖 com.codingapi.txlcn txlcn-tc 5.0.2.RELEASE com.codingapi.txlcn txlcn-txmsg-netty 5.0.2.RELEASE net.sf.json-lib json-lib 2.4 jdk15 org.springframework.cloud spring-cloud-starter-netflix-eureka-client 2.2.2.RELEASE org.mybatis.spring.boot mybatis-spring-boot-starter 2.0.0 mysql mysql-connector-java com.alibaba druid 1.1.12 写配置 server: port: 1000 # 服务名称 spring: application: name: lcn-order datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/lcn-order?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: wk3515134 dbcp2: initial-size: 5 min-idle: 5 max-total: 5 max-wait-millis: 200 validation-query: SELECT 1 test-while-idle: true test-on-borrow: false test-on-return: false mybatis: mapper-locations: - classpath:mapper/*.xml eureka: client: service-url: # 注册进eureka defaultZone: http://euk-server-one.com:8080/eureka/ # 事务处理器地址,需要先启动lcn-tm系统并登录,端口就在下图所示位置 tx-lcn: client: manager-address: 127.0.0.1:3100

在这里插入图片描述

加注解

在lcn-order工程启动类添加注解

@EnableDistributedTransaction 启动类定义RestTemplate的bean(用于调用lcn-pay系统方法) @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } 添加测试Controller类(OrderController) import com.codingapi.txlcn.tc.annotation.LcnTransaction; import com.hepai.lcnorder.dao.TblOrderDao; import com.hepai.lcnorder.entity.TblOrder; import net.sf.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController public class OrderController { @Autowired private TblOrderDao tblOrderDao; @Autowired private RestTemplate restTemplate; @PostMapping("/add-order") @Transactional(rollbackFor = Exception.class) //一定要加,LCN源码会判断这个注解 @LcnTransaction // 注解必须加,使用LCN模式,加在有调用分布式系统的方法之上即可 public String add(@RequestBody TblOrder bean){ JSONObject date = new JSONObject(); date.put("payName",bean.getOrderName()+"pay"); // 调用支付系统指定方法 restTemplate.postForEntity("http://lcn-pay/add-pay",date,String.class); // int i = 1/0; // 插入sql,Service、Mapper以及Entity大家自己写吧,按照普通业务逻辑写就好 tblOrderDao.insert(bean); return "新增订单成功"; } } 支付系统(lcn-pay) 引依赖(与lcn-order系统依赖相同) com.codingapi.txlcn txlcn-tc 5.0.2.RELEASE com.codingapi.txlcn txlcn-txmsg-netty 5.0.2.RELEASE net.sf.json-lib json-lib 2.4 jdk15 org.springframework.cloud spring-cloud-starter-netflix-eureka-client 2.2.2.RELEASE org.mybatis.spring.boot mybatis-spring-boot-starter 2.0.0 mysql mysql-connector-java com.alibaba druid 1.1.12 写配置 server: port: 2000 # 服务名 spring: application: name: lcn-pay datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/lcn-pay?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: wk3515134 dbcp2: initial-size: 5 min-idle: 5 max-total: 5 max-wait-millis: 200 validation-query: SELECT 1 test-while-idle: true test-on-borrow: false test-on-return: false mybatis: mapper-locations: - classpath:mapper/*.xml eureka: client: service-url: defaultZone: http://euk-server-one.com:8080/eureka/ # 事务处理器地址,需要先启动lcn-tm系统并登录,端口就在下图所示位置 tx-lcn: client: manager-address: 127.0.0.1:3100

在这里插入图片描述

加注解

在lcn-pay工程启动类添加注解

@EnableDistributedTransaction 添加测试Controller类(PayController) import com.codingapi.txlcn.tc.annotation.LcnTransaction; import com.hepai.lcnpay.dao.TblPayDao; import com.hepai.lcnpay.entity.TblPay; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class PayController { @Autowired private TblPayDao tblPayDao; @PostMapping("/add-pay") @Transactional(rollbackFor = Exception.class) @LcnTransaction public String addPay(@RequestBody TblPay bean){ tblPayDao.insert(bean); return "新增支付成功"; } } 执行测试 测试事务执行成功 使用Postman调用【http://localhost:1000/add-order】,填写相应参数,执行结果: 在这里插入图片描述查看数据库数据: 在这里插入图片描述 测试事务执行失败 修改OderController类方法,添加异常 public String add(@RequestBody TblOrder bean){ JSONObject date = new JSONObject(); date.put("payName",bean.getOrderName()+"pay"); restTemplate.postForEntity("http://lcn-pay/add-pay",date,String.class); // 添加异常 int i = 1/0; tblOrderDao.insert(bean); return "新增订单成功"; }

使用Postman调用【http://localhost:1000/add-order】,填写相应参数,执行结果: 在这里插入图片描述

查看数据库数据: 在这里插入图片描述 可见,数据库数据没有变化,还是刚才事务执行成功插入的数据,所以,分布式事务生效。

TCC 原理及主要特点 TCC模式在实现的时候,每个事务参与系统的业务类都需要写三个方法(业务执行方法、提交方法、取消方法),即,在【执行业务】的时候,如果分布式事务整体失败,就执行【取消方法】,如果分布式事务整体成功,就执行【提交方法】,加大了代码实现的复杂度。TCC模式适用于无本地事务控制的情况,比如:MongoDB、Redis等不支持事务的中间件,就可以使用TCC模式来实现分布式事务。TCC模式主要也是分为两个阶段,第一个阶段,各事务参与者本地直接执行SQL并提交,并反馈执行结果给事务管理者;第二阶段,事务管理者根据第一阶段的反馈判断所有事务参与者是执行【取消方法】还是执行【提交方法】。如果第二阶段执行【取消方法】,需要执行第一阶段SQL的逆SQL,也就是像Mysql的Undo日志相似,第一阶段执行的insert语句需要delete来恢复,update语句需要逆update来恢复,更加大了业务的复杂度和代码的实现难度。

注意: TCC模式第一阶段执行完SQL是直接提交的,逆SQL的准备是非常有必要的。

代码实现(以LCN测试代码为基础添加TCC代码) 订单系统(lcn-order) 新增测试Controller类(OrderTccController) import com.codingapi.txlcn.tc.annotation.TccTransaction; import com.hepai.lcnorder.dao.TblOrderDao; import com.hepai.lcnorder.entity.TblOrder; import net.sf.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.Map; @RestController public class OrderTccController { @Autowired private TblOrderDao tblOrderDao; @Autowired private RestTemplate restTemplate; @PostMapping("/add-order-tcc") @Transactional(rollbackFor = Exception.class) @TccTransaction // 使用框架的TCC模式实现分布式事务 public String add(@RequestBody TblOrder bean){ JSONObject date = new JSONObject(); date.put("payName",bean.getOrderName()+"pay"); restTemplate.postForEntity("http://lcn-pay/add-pay-tcc",date,String.class); tblOrderDao.insert(bean); Integer id = bean.getId(); maps.put("a",id); // int i = 1/0; return "新增订单成功1"; } public String confirmAdd(TblOrder bean){ System.out.println("order confirm "); return "新增订单成功2"; } private static Map maps = new HashMap(); public String cancelAdd(TblOrder bean){ Integer a = maps.get("a"); System.out.println("a:"+a); // 取消方法执行sql逆操作 tblOrderDao.deleteByPrimaryKey(a); System.out.println("order cancel "); return "新增订单失败"; } }

注意: cancelAdd()和confirmAdd()不用加注解,因为框架源码中表明如果没有发现【提交方法】和【取消方法】的注解,那就默认把加了【@TccTransaction】注解的方法名第一个字母大写并加前缀【cancel】和【confirm】来分别作为【提交方法】和【取消方法】的方法名。

支付系统(lcn-pay) 新增测试Controller类(PayTccController) import com.codingapi.txlcn.tc.annotation.TccTransaction; import com.hepai.lcnpay.dao.TblPayDao; import com.hepai.lcnpay.entity.TblPay; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController public class PayTccController { @Autowired private TblPayDao tblPayDao; @PostMapping("/add-pay-tcc") @Transactional(rollbackFor = Exception.class) @TccTransaction public String addPay(@RequestBody TblPay bean){ tblPayDao.insert(bean); Integer id = bean.getId(); maps.put("a",id); int i = 1/0; return "新增支付成功1"; } public String confirmAddPay(TblPay bean){ System.out.println("pay confirm"); return "新增支付成功2"; } private static Map maps = new HashMap(); /** * 逆sql * @param bean * @return */ public String cancelAddPay(TblPay bean){ Integer a = maps.get("a"); System.out.println("a:"+a); System.out.println("pay cancel"); tblPayDao.deleteByPrimaryKey(a); return "支付失败"; } } 执行测试 测试事务执行成功

使用Postman调用【http://localhost:1000/add-order-tcc】,填写相应参数,执行结果: 在这里插入图片描述

查看数据库数据: 在这里插入图片描述

测试事务执行失败 修改OderTccController类方法,添加异常 public String add(@RequestBody TblOrder bean){ JSONObject date = new JSONObject(); date.put("payName",bean.getOrderName()+"pay"); restTemplate.postForEntity("http://lcn-pay/add-pay-tcc",date,String.class); tblOrderDao.insert(bean); Integer id = bean.getId(); maps.put("a",id); // 添加异常 int i = 1/0; return "新增订单成功1"; }

使用Postman调用【http://localhost:1000/add-order-tcc】,填写相应参数,执行结果: 在这里插入图片描述

查看数据库数据: 在这里插入图片描述 可见,数据库数据没有变化,还是刚才事务执行成功插入的数据,所以,分布式事务生效。

TXC 原理及主要特点 TXC模式的工作流程中执行SQL前要先查询影响的数据,如果数据量大的情况下,效率会极低。TXC模式只能支持SQL方式的事务模块,因为第一条,它必须要先查询影响数据,所以有要执行的SQL是大前提。不会占用数据库的连接资源。我个人认为,对于第一条,如果是有大量的数据,并且涉及到的影响数据表有成百上千个,那这种方式是最不建议的,这种模式对查询的效率要求极高。 总结

在TX-LCN框架中,在参与者本地支持事务的分布式事务情况下,建议使用LCN模式,参与者本地有不支持事务的情况下,只能用TCC。

源码地址

为防大家使用的时候还是出现其他问题,我把源码共享出来,希望能帮助大家。

https://gitee.com/hepai123/lcn-demo.git


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有