TX-LCN分布式事务框架(原理、案例、源码)
案例源码 https://gitee.com/BanSheng/learn-lcn
一、 什么是分布式事务
分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。
举个栗子:
电商系统中的订单系统与库存系统
图中包含了库存和订单两个独立的微服务,每个微服务维护了自己的数据库。在交易系统的业务逻辑中,一个商品在下单之前需要先调用库存服务,进行扣除库存,再调用订单服务,创建订单记录。
正常情况下,两个数据库各自更新成功,两边数据维持着一致性。如果在非正常情况下,有可能库存的扣减完成了,随后的订单记录却因为某些原因插入失败。或者是订单创建成功了,但是库存扣除商品的数据量失败了,这个时候,两边数据就失去了应有的一致性。
这时候就需要保证事务的一致性了,单数据源的用单机事务来保证。多数据源就需要依赖分布式事务来处理。
二、 XA 的两阶段提交方案
1 什么是 XA 协议
XA 协议由 Oracle Tuxedo 首先提出的,并交给 X/Open 组织,作为资源管理器(数据库)与事务管理器的接口标准。目前,Oracle、Informix、DB2 和 Sybase 等各大数据库厂家都提供对 XA 的支持。XA 协议采用两阶段提交方式来管理分布式事务。XA 接口提供资源管理器与事务管理器之间进行通信的标准接口。
XA 就是 X/Open DTP 定义的交易中间件与数据库之间的接口规范(即接口函数),交易中间件用它来通知数据库事务的开始、结束以及提交、回滚等。XA 接口函数由数据库厂商提供。
X/Open 组织(即现在的 Open Group)定义了分布式事务处理模型。X/Open DTP 模型(1994)包括应用程序(AP)、事务管理器(TM)、资源管理器(RM)、通信资源管理器(CRM)四部分。一般,常见的事务管理器(TM)是交易中间件,常见的资源管理器(RM)是数据库,常见的通信资源管理器(CRM)是消息中间件。
2 XA 协议的一阶段提交
如果在程序中开启了事务,那么在应用程序发出提交/回滚请求后,数据库执行操作,而后将成功/失败返回给应用程序,程序继续执行。
一阶段提交协议相对简单。优点也很直观,它不用再与其他的对象交互,节省了判断步骤和时间,所以在性能上是在阶段提交协议中最好的。但缺点也很明显:数据库确认执行事务的时间较长,出问题的可能性就随之增大。如果有多个数据源,一阶段提交协议无法协调他们之间的关系。
3 XA 协议的二阶段提交
在一阶段协议的基础上,有了二阶段协议,二阶段协议的好处是添加了一个管理者角色。
很明显,二阶段协议通过将两层变为三层,增加了中间的管理者角色,从而协调多个数
据源之间的关系,二阶段提交协议分为两个阶段。
应用程序调用了事务管理器的提交方法,此后第一阶段分为两个步骤:
事务管理器通知参与该事务的各个资源管理器,通知他们开始准备事务。
资源管理器接收到消息后开始准备阶段,写好事务日志并执行事务,但不提交,然后将
是否就绪的消息返回给事务管理器(此时已经将事务的大部分事情做完,以后的内容耗时极小)。
第二阶段也分为两个步骤:
事务管理器在接受各个消息后,开始分析,如果有任意其一失败,则发送回滚命令,否则发送提交命令。
各个资源管理器接收到命令后,执行(耗时很少),并将提交消息返回给事务管理器。
事务管理器接受消息后,事务结束,应用程序继续执行。
为什么要分两步执行?
一是因为分两步,就有了事务管理器统一管理的机会;
二尽可能晚地提交事务,让事务在提交前尽可能地完成所有能完成的工作,这样,最后的提交阶段将是耗时极短,耗时极短意味着操作失败的可能性也就降低。
同时,二阶段提交协议为了保证事务的一致性,不管是事务管理器还是各个资源管理器,每执行一步操作,都会记录日志,为出现故障后的恢复准备依据。
缺点:
1 二阶段提交协议的存在的弊端是阻塞,因为事务管理器要收集各个资源管理器的响应消息,如果其中一个或多个一直不返回消息,则事务管理器一直等待,应用程序也被阻塞,甚至可能永久阻塞。
2 两阶段提交理论的一个广泛工业应用是 XA 协议。目前几乎所有收费的商业数据库都支持 XA 协议。XA 协议已在业界成熟运行数十年,但目前它在互联网海量流量的应用场景中,吞吐量这个瓶颈变得十分致命,因此很少被用到。
三、 TCC 解决方案
1 TCC 介绍
TCC 是由支付宝架构师提供的一种柔性解决分布式事务解决方案,主要包括三个步骤
Try:预留业务资源/数据效验
Confirm:确认执行业务操作
Cancel:取消执行业务操作
2 TCC 原理
TCC 方案在电商、金融领域落地较多。TCC 方案其实是两阶段提交的一种改进。其
将整个业务逻辑的每个分支显式的分成了 Try、Confirm、Cancel 三个操作。Try 部分完成
业务的准备工作,confirm 部分完成业务的提交,cancel 部分完成事务的回滚。基本原理如
下图所示。
事务开始时,业务应用会向事务协调器注册启动事务。之后业务应用会调用所有服务的 try 接口,完成一阶段准备。之后事务协调器会根据 try 接口返回情况,决定调用 confirm接口或者 cancel 接口。如果接口调用失败,会进行重试。
微服务倡导服务的轻量化、易部署,而 TCC 方案中很多事务的处理逻辑需要应用自己编码实现,复杂且开发量大
3 TCC 的关键流程如下图( 以创建订单和扣减库存为例子)
4 TCC 优缺点
4.1TCC 优点:
让应用自己定义数据库操作的粒度,使得降低锁冲突、提高吞吐量成为可能。
4.2TCC 不足之处:
对应用的侵入性强。业务逻辑的每个分支都需要实现 try、confirm、cancel三个操作,应用侵入性较强,改造成本高。
实现难度较大。需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。为了满足一致性的要求,confirm 和 cancel 接口必须实现幂等。
四、 分布式事务中间件解决方案
分布式事务中间件其本身并不创建事务,而是基于对本地事务的协调从而达到事务一致性的效果。典型代表有:阿里的 GTS(https://www.aliyun.com/aliware/txc)、开源应用 LCN。其实现原理如下:
五、 是 什么是 LCN 框架
1 LCN 框架的由来
LCN 并不生产事务,LCN 只是本地事务的协调工
在设计框架之初的 1.0 ~ 2.0 的版本时,框架设计的步骤是如下的,各取其首字母得来的 LCN 命名。
锁定事务单元(lock)、确认事务模块状态(confirm)、通知事务(notify)
2 LCN 框架相关资料
tx-lcn 官方地址:https://www.txlcn.org/
tx-lcn Github 地址:https://github.com/codingapi/tx-lcn
tx-lcn 服务下载地址:https://pan.baidu.com/s/1cLKAeE#list/path=%2F
tx-lcn 服务源码地址:https://github.com/codingapi/tx-lcn/tree/master/tx-manager
六、 LCN 框架 原理及 执行步骤
1 LCN 的执行原理
1.1LCN 原理
在上图中,微服务 A,微服务 B,TxManager 事务协调器,都需要去 Eureka 中注册服务。Eureka 是用于 TxManager 与其他服务之间的相互服务发现。redis 是用于存放我们事务组的信息以及补偿的信息。然后微服务 A 与微服务 B 他们都需要去配置上我们 TxClient 的包架构(代码的包架构);来支持我们的 LCN 框架,以及他们的数据库。
2 LCN 执行步骤
2.1 创建事务组 :
事务组是指的我们在整个事务过程中把各个节点(微服务)单元的事务信息存储在一个固定单元里。但这个信息并不是代表是事务信息,而是只是作为一个模块的标示信息。创建事务组是指在事务发起方开始执行业务代码之前先调用 TxManager 创建事务组对象,然后拿到事务标示 GroupId 的过程。
2.2 添加事务组 :
添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息添加通知给TxManager 的操作。
2.3 关闭事务组 :
是指在发起方执行完业务代码以后,将发起方执行结果状态通知给 TxManager 的动作。当执行完关闭事务组的方法以后,TxManager 将根据事务组信息来通知相应的参与模块提交或回滚事务。
2.4业务执行流程图
七、 是 什么是 LCN 的事务协调机制
如图:假设服务已经执行到关闭事务组的过程,那么接下来作为一个模块执行通知给TxManager,然后告诉他本次事务已经完成。那么如图中 Txmanager 下一个动作就是通过事务组的 id,获取到本次事务组的事务信息;然后查看一下对应有那几个模块参与,然后如果是有 A/B/C 三个模块;那么对应的对三个模块做通知、提交、回滚。
那么提交的时候是提交给谁呢?
是提交给了我们的 TxClient 模块。然后 TxCliient 模块下有一个连接池,就是框架自定义的一个连接池(如图 DB 连接池);这个连接池其实就是在没有通知事务之前一直占有着这次事务的连接资源,就是没有释放。但是他在切面里面执行了 close 方法。在执行 close的时候。如果需要(TxManager)分布式事务框架的连接。他被叫做“假关闭”,也就是没有关闭,只是在执行了一次关闭方法。实际的资源是没有释放的。这个资源是掌握在 LCN 的连接池里的。
然后当 TxManager 通知提交或事务回滚的时候呢?
TxManager 会通知我们的 TxClient 端。然后 TxClient 会去执行相应的提交或回滚。提交或回滚之后再去关闭连接。这就是LCN 的事务协调机制。说白了就是代理 DataSource 的机制;相当于是拦截了一下连接池,控制了连接池的事务提交。
八、 是 什么是 LCN 的事务补偿机制
1 什么是补偿事务机制?
LCN 的补偿事务原理是模拟上次失败事务的请求,然后传递给 TxClient 模块然后再次执行该次请求事务。
简单的说:lcn 事务补偿是指在服务挂机和网络抖动情况下 txManager 无法通知事务单元时。(通知不到也就两种原因服务挂了和网络出问题)在这种情况下 TxManager 会做一个标示;然后返回给发起方。告诉他本次事务有存在没有通知到的情况。
那么如果是接收到这个信息之后呢,发起方就会做一个标示,标示本次事务是需要补偿事务的。这就是事务补偿机制。
2 为什么需要事务补偿?
事务补偿是指在执行某个业务方法时,本应该执行成功的操作却因为服务器挂机或者网络抖动等问题导致事务没有正常提交,此种场景就需要通过补偿来完成事务,从而达到事务的一致性。
3 补偿机制的触发条件?
当执行关闭事务组步骤时,若发起方接受到失败的状态后将会把该次事务识别为待补偿事务,然后发起方将该次事务数据异步通知给 TxManager。TxManager 接受到补偿事务以后先通知补偿回调地址,然后再根据是否开启自动补偿事务状态来补偿或保存该次切面事务数据。
九、 LCN 分布式事务框架应用
案例gitee源码:https://gitee.com/BanSheng/learn-lcn
1 LCN 应用案例设计
1.1 需求
创建三个服务分别为:springcloud-portal、springcloud-order、springcloud-inventory。
在springcloud-portal 服务中处理创建订单的请求,然后分别请求 springcloud-order 以及springcloud-inventory服务。在springcloud-order中插入一条订单数据,在springcloud-inventory中对商品的数量做更新。
1.2 使用技术
数据库:Mysql
开发平台:SpringCloud+SpringBoot+MyBatis
1.3 数据库设计
创建两个数据库分别为:ld_orders,ld_inventory。springcloud-order 操作 ld_orders 库,
springclooud-inventory 操作 ld_inventory 库
1.3.1 ld_orders 库中的表设计
在 ld_orders 库中包含一个 tb_orders 表。表结构如下:
CREATE TABLE `tb_orders` (
`orderid` int(11) NOT NULLAUTO_INCREMENT,
`itemid` int(11) DEFAULT NULL,
`price` int(11) DEFAULT NULL,
PRIMARY KEY (`orderid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
1.3.2 ld_inventory 库中表设计
在 ld_inventory 库中包含一个 tb_inventory 表。表结构如下:
CREATE TABLE `tb_inventory` (
`inventoryid` int(11) NOT NULLAUTO_INCREMENT,
`itemid` int(11) DEFAULT NULL,
`itemnum` int(11) DEFAULT NULL,
PRIMARY KEY (`inventoryid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2 创建服务
2.1 创建项目
2.1.1springcloud-portal
pom 文件
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.18.RELEASE</version>
</parent>
<groupId>com.ld</groupId>
<artifactId>springcloud-portal</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId
>
</dependency>
<!-- 添加 Feign 坐标 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
application.properties 配置文件
spring.application.name=springcloud-portal
server.port=8080
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
2.1.2 springcloud-order
pom 文件
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.18.RELEASE</version>
</parent>
<groupId>com.ld</groupId>
<artifactId>springcloud-order</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId
>
</dependency>
<!-- 添加 Feign 坐标 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Mybatis 启动器 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId
>
<version>1.1.1</version>
</dependency>
<!-- mysql 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- druid 数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
</dependencies>
</project>
application.properties 配置文件
spring.application.name=springcloud-order
server.port=8181
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ld_orders
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.type-aliases-package=com.ld.pojo
2.1.3 springcloud-inventory
pom 文件
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.18.RELEASE</version>
</parent>
<groupId>com.ld</groupId>
<artifactId>springcloud-inventory</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId
>
</dependency>
<!-- 添加 Feign 坐标 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Mybatis 启动器 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId
>
<version>1.1.1</version>
</dependency>
<!-- mysql 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- druid 数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
</dependencies>
</project>
application.properties 配置文件
spring.application.name=springcloud-inventory
server.port=8282
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ld_inventory
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.type-aliases-package=com.ld.pojo
2.2 创建服务接口
2.2.1 springcloud-order-service
pom 文件
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.18.RELEASE</version>
</parent>
<groupId>com.ld</groupId>
<artifactId>springcloud-order-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
2.2.2 springcloud-inventory-service
pom 文件
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.18.RELEASE</version>
</parent>
<groupId>com.ld</groupId>
<artifactId>springcloud-inventory-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
修改 springcloud-inventory 服务的 pom 文件
<dependency>
<groupId>com.ld</groupId>
<artifactId>springcloud-inventory-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
修改 springcloud-order 服务的 pom 文件
<dependency>
<groupId>com.ld</groupId>
<artifactId>springcloud-order-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
修改 springcloud-portal 服务的 pom 文件
<dependency>
<groupId>com.ld</groupId>
<artifactId>springcloud-order-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.ld</groupId>
<artifactId>springcloud-inventory-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
2.3 实现业务
代码可参考:https://gitee.com/BanSheng/learn-lcn
2.3.1 实现添加订单业务
2.3.1.1 添加 mapper
2.3.1.2 添加业务层
2.3.1.3 添加 controller
2.3.1.4 添加启动类
2.3.1.5 在业务接口项目中添加 pojo
2.3.2 实现修改库存商品数量业务
2.3.2.1 添加 mapper
2.3.2.2 添加业务层
2.3.2.3 添加 controller
2.3.2.4 添加启动类
2.3.2.5 在业务接口项目中添加 pojo
2.4 实现 portal 中的创建订单业务
2.4.1 添加实现服务接口的接口
2.4.2 添加 portalService 接口
2.4.3 添加 controller
2.4.4 添加启动类
2.5 测试
目前是不具备分布式事务处理能力的。
3 使用 LCN 实现分布式事务处理
3.1 下载 LCN
3.2 配置事务协调器
3.2.1 将事务协调器服务添加到 IDE 中
3.2.2 配置事务协调器
########txmanager-start##############
#服务端口
server.port=8888
#tx-manager 不得修改
spring.application.name=tx-manager
spring.mvc.static-path-pattern=/**
spring.resources.static-locations=classpath:/static/
#######txmanager-end##############
#zookeeper 地址
#spring.cloud.zookeeper.connect-string=127.0.0.1:2181
#spring.cloud.zookeeper.discovery.preferIpAddress= true
#eureka 地址
eureka.client.service-url.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
#########redis-start##############
#redis 配置文件,根据情况选择集群或者单机模式
##redis 集群环境配置
##redis cluster
#spring.redis.cluster.nodes=127.0.0.1:7001,127.0.0.1:7002,127.0.0.1:7003
#spring.redis.cluster.commandTimeout=5000
##redis 单点环境配置
#redis
#redis 主机地址
spring.redis.host=192.168.70.145
#redis 主机端口
spring.redis.port=6379
#redis 链接密码
spring.redis.password=
spring.redis.pool.maxActive=10
spring.redis.pool.maxWait=-1
spring.redis.pool.maxIdle=5
spring.redis.pool.minIdle=0
spring.redis.timeout=0
#########redis-end##########
########LCN-start###########
#业务模块与 TxManager 之间通讯的最大等待时间(单位:秒)
#通讯时间是指:发起方与响应方之间完成一次的通讯时间。
#该字段代表的是 Tx-Client 模块与 TxManager 模块之间的最大通讯时间,超过该时间未响应本次请求失败。
tm.transaction.netty.delaytime = 5
#业务模块与 TxManager 之间通讯的心跳时间(单位:秒)
tm.transaction.netty.hearttime = 15
#存储到 redis 下的数据最大保存时间(单位:秒)
#该字段仅代表的事务模块数据的最大保存时间,补偿数据会永久保存。
tm.redis.savemaxtime=30
#socket server Socket 对外服务端口
#TxManager 的 LCN 协议的端口
tm.socket.port=9999
#最大 socket 连接数
#TxManager 最大允许的建立连接数量
tm.socket.maxconnection=100
#事务自动补偿 (true:开启,false:关闭)
# 说明:
# 开启自动补偿以后,必须要配置tm.compensate.notifyUrl 地址,仅当tm.compensate.notifyUrl 在请求补偿确认时返回 success 或者SUCCESS 时,才会执行自动补偿,否则不会自动补偿。
# 关闭自动补偿,当出现数据时也会tm.compensate.notifyUrl 地址。
# 当 tm.compensate.notifyUrl 无效时,不影响 TxManager运行,仅会影响自动补偿。
tm.compensate.auto=false
#事务补偿记录回调地址(rest api 地址,post json 格式)
#请求补偿是在开启自动补偿时才会请求的地址。请求分为两种:1.补偿决策,2.补偿结果通知,可通过通过 action 参数区分compensate 为补偿请求、notify 为补偿通知。
#*注意当请求补偿决策时,需要补偿服务返回"SUCCESS"字符串以后才可以执行自动补偿。
#请求补偿结果通知则只需要接受通知即可。
#请求补偿的样例数据格式:
#{"groupId":"TtQxTwJP","action":"compensate","json":"{\"address\":\"133.133.5.100:8081\",\"className\":\"com.example.demo.service.impl.DemoServiceImpl\",\"currentTime\":1511356150413,\"data\":\"C5IBLWNvbS5leGFtcGxlLmRlbW8uc2VydmljZS5pbXBsLkRlbW9TZXJ2aWNlSW1wbAwSBHNhdmUbehBqYXZhLmxhbmcuT2JqZWN0GAAQARwjeg9qYXZhLmxhbmcuQ2xhc3MYABABJCo/cHVibGljIGludCBjb20uZXhhbXBsZS5kZW1vLnNlcnZpY2UuaW1wbC5EZW1vU2VydmljZUltcGwuc2F2ZSgp\",\"groupId\":\"TtQxTwJP\",\"methodStr\":\"public intcom.example.demo.service.impl.DemoServiceImpl.save()\",\"model\":\"demo1\",\"state\":0,\"time\":36,\"txGroup\":{\"groupId\":\"TtQxTwJP\",\"hasOver\":1,\"isCompensate\":0,\"list\":[{\"address\":\"133.133.5.100:8899\",\"isCompensate\":0,\"isGroup\":0,\"kid\":\"wnlEJoSl\",\"methodStr\":\"public int
com.example.demo.service.impl.DemoServiceImpl.save()\",\"model\":\"demo2\",\"modelIpAddress\":\"133.133.5.100:8082\",\"channelAddress\":\"/133.133.5.100:64153\
",\"notify\":1,\"uniqueKey\":\"bc13881a5d2ab2ace89ae5d34d608447\"}],\"nowTime\":0,\"startTime\":1511356150379,\"state\":1},\"uniqueKey\":\"be6eea31e382f1f0878d07cef319e4d7\"}"}
#请求补偿的返回数据样例数据格式:
#SUCCESS
#请求补偿结果通知的样例数据格式:
#{"resState":true,"groupId":"TtQxTwJP","action":"notify"}
tm.compensate.notifyUrl=http://ip:port/path
#补偿失败,再次尝试间隔(秒),最大尝试次数 3 次,当超过3 次即为补偿失败,失败的数据依旧还会存在 TxManager 下。
tm.compensate.tryTime=30
#各事务模块自动补偿的时间上限(毫秒)
#指的是模块执行自动超时的最大时间,该最大时间若过段会导致事务机制异常,该时间必须要模块之间通讯的最大超过时间。
#例如,若模块 A 与模块 B,请求超时的最大时间是 5 秒,则建议改时间至少大于 5 秒。
tm.compensate.maxWaitTime=5000
########LCN-end####
logging.level.com.codingapi=debug
3.3 访问 TX-Manager 事务协调器的管理页面
访问地址:http://ip:port
3.4 创建 TX-Client
3.4.1 添加坐标
在所有需要处理分布式事务的微服务中增加下述依赖:为统一资源版本,使用properties 统一管理版本信息。
<properties>
<lcn.last.version>4.1.0</lcn.last.version>
</properties>
<dependency>
<groupId>com.codingapi</groupId>
<artifactId>transaction-springcloud</artifactId>
<version>${lcn.last.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.codingapi</groupId>
<artifactId>tx-plugins-db</artifactId>
<version>${lcn.last.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
3.4.2 实现接口
使用 LCN 做分布式事务管理时,服务需要知道事务协调器的 URL(客户端与事务协调器是通过 URL 来建立连接的)。我们需要实现一个接口 TxManagerTxUrlService,这个接口实现类可以使用独立应用定义,在微服务应用中引入
@Service
public class TxManagerTxUrlServiceImpl implements TxManagerTxUrlService {
@Value("${tm.manager.url}")
private String url;
@Override
public String getTxUrl() {
return url;
}
}
3.4.3 创建事务客户端项目
3.4.4 修改 pom 文件
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/
4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ld</groupId>
<artifactId>tx-trans-manager-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<lcn.last.version>4.1.0</lcn.last.version>
</properties>
<dependencies>
<dependency>
<groupId>com.codingapi</groupId>
<artifactId>transaction-springcloud</artifactId>
<version>${lcn.last.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.codingapi</groupId>
<artifactId>tx-plugins-db</artifactId>
<version>${lcn.last.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
3.4.5 在项目中创建 TxManagerTxUrlService 接口的实现类
@Service
public class TxManagerTxUrlServiceImpl implements
TxManagerTxUrlService {
@Value("${tm.manager.url}")
private String url;
@Override
public String getTxUrl() {
// "http://localhost:8888/tx/manager/";
return url;
}
}
3.4.6 完成项目的注入
将 tx-trans-manager-service 项目分别注入给 springcloud-inventory、springcloud-order、
springcloud-portal
3.4.7 修改客户端服务的配置文件
在客户端服务的全局配置文件中增加下述配置:
# 定义事务协调器所在位置。根据具体环境定义其中的 IP 地址和端口。
tm.manager.url=http://127.0.0.1:8899/tx/manager/
3.5 使用 LCN 提供的注解实现分布式事务处理
3.5.1 @TxTransaction
3.5.1.1 在整个业务的事务 代码中 添加 注解@TxTransaction 。
3.5.1.2 在 事 务 的 起 始 代码 中 的 @TxTransaction 注 解中添加 isStart=true
3.5.2 测试
十、 置 在服务网关中配置 LCN
1 创建服务网关
1.1创建项目
1.1.1 修改 pom 文件
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/
4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.18.RELEASE</version>
</parent>
<groupId>com.ld</groupId>
<artifactId>springcloud-zuul</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
1.1.2 编写启动类
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class,
args);
}
}
1.1.3 添加全局配置文件
spring.application.name=springcloud-zuul
server.port=9090
2 在服务网关中配置 LCN
2.1 需改服务网关配置文件
#Eureka 注册中心地址
eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/
#路由规则:采用服务名称指定路由方式
zuul.routes.tx-manager.path=/tx/**
zuul.routes.tx-manager.serviceId=tx-manager
2.2 修改 TX客户端服务配置文件
#http://服务网关 IP:服务网关端口
tm.manager.url=http://localhost:9090/tx/tx/manager/
2.3 测试
案例gitee源码:https://gitee.com/BanSheng/learn-lcn
本文地址:https://blog.csdn.net/donglinjob/article/details/107490085