写在前面:
由于工作需要,公司的微服务项目需解决分布式事务的问题,且由我进行分布式事务框架搭建和整合工作。
那么借此机会好好的将解决分布式事务的内容进行整理一下。这边公司分布式事务框架选型是LCN框架(以后肯定会升级成seata)。
我整理的大纲如下:
1 CAP定律和BASE理论
有人问,为什么需要了解这个,这个其实是分布式事务基于的理论依据,所以需要了解一下。
1.1 CAP定律
这个定理的内容是指的是在一个分布式系统中、Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
(一)一致性(C)
在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
(二)可用性(A)
在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
(三)分区容错性(P)
以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。
(四)总结一下
以上可以知道分区容错性(P)主要代表网络波动产生的错误,这是不可避免的,且这个三个模式不可兼得,所以目前就只有两种模式:CP和AP模式。
其中CP表示遵循一致性原则,但不能保证高可用性,其中zookeeper作为注册中心就是采用CP模式,因为zookeeper有过半节点不可以的话整个zookeeper将不可用。
AP表示遵循于可用性原则,例如Eureka作为注册中心用的是AP模式,因为其为去中心化,采用你中有我我中有你的相互注册方式,只要集群中有一个节点可以使用,整个eureka服务就是可用的,但可能会出现短暂的数据不一致问题。
1.2 BASE理论
BASE是Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的缩写。BASE理论是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结, 是基于CAP定理逐步演化而来的。BASE理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。
(一)基本可用
基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性,注意,这绝不等价于系统不可用。
比如:响应时间上的损失。正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果,但由于出现故障,查询结果的响应时间增加了1~2秒。
系统功能上的损失:正常情况下,在一个电子商务网站上进行购物的时候,消费者几乎能够顺利完成每一笔订单,但是在一些节日大促购物高峰的时候,由于消费者的购物行为激增,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面。
(二)软状态
软状态指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。
(三)最终一致性
最终一致性强调的是所有的数据副本,在经过一段时间的同步之后,最终都能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
2 了解分布式事务
2.1 分布式事务产生的背景
在分布式产生之前,互联网公司的项目都是传统的单体项目,整个项目都共用了一个数据源,那么进行业务执行对数据库进行操作的时候,不会产生这种事务不一致问题,因为是同一个数据源用的一个本地事务。
但随着我们架构的演变,从单体架构到分布式架构到SOA架构项目再到如今公司采用的微服务架构,我们对服务进行了业务拆分,那么数据源也被拆分,一般微服务项目是一个服务对应一个数据源。
那么在这种架构下,每个服务都有自己独立的数据源有自己的本地事务,从而便会产生分布式事务,可能造成服务间数据不一致问题。
2.2 不同架构体系下解决事务的问题
(一)单体架构(单数据源)
在单体的项目中,多个不同业务逻辑都是在同一个数据源中实现事务管理,是不存在分布式事务的问题,因为同一数据源的情况下都是采用事务管理器,相当于每个事务管理器对应一个数据源。
(一)单体架构(多数据源)
在单体的项目中,有多个不同的数据源,每个数据源中都有自己独立的事务管理器,互不影响,那么这时候也会存在多数据源事务管理:解决方案jta+ Atomikos。
(一)分布式/微服务架构
在分布式/微服务架构,每个服务都有自己独立的数据源和事务管理器,那么在这种情况下如果有业务要进行RPC远程调用的时候,那就必然可能产生分布式事务。目前主要解决方案有:MQ、LCN、Seata等方案。
3 LCN解决分布式事务
3.1 了解LCN
3.1.1 LCN背景
LCN框架在2017年6月份发布第一个版本,从开始的1.0,已经发展到了5.0版本。
LCN名称是由早期版本的LCN框架命名,在设计框架之初的1.0 ~ 2.0的版本时框架设计的步骤是如下,各取其首字母得来的LCN命名。
5.0以后由于框架兼容了LCN、TCC、TXC三种事务模式,为了避免区分LCN模式,特此将LCN分布式事务改名为TX-LCN分布式事务框架。
3.1.2 LCN定位
TX-LCN定位于一款事务协调性框架,框架其本身并不操作事务,而是基于对事务的协调从而达到事务一致性的效果。(LCN不生产事务,它只是事务的搬运工...woc这有点像农夫山泉的文案)
3.2 LCN分布式事务原理(自己理解的,白话文通俗易懂)
(上图来源官网)
1) 首先我们的lcn协调者(TM)会和lcn客户端(TC)通过引入的netty一直保持着长连接(持续监听)。
2) 当请求的发起方(调用方)进入接口业务之前,会通过AOP技术进到@LcnTransaction注解中去LCN协调者那边生成注册一个全局的事务组Id(groupId)。
3) 当发起方(调用方)通过rpc调用参与方(被调用方)的时候,lcn重写了Feign客户端,会从ThreadLocal中拿到该事务组Id(groupId),并将该事务组Id设置到请求头中。
4) 参与方(被调用方)在请求头中获取到了这个groupId的时候,lcn会标识该服务为参与方并加入到该事务组,并会被lcn代理数据源,当该服务业务逻辑执行完成后,进行数据源的假关闭,并不会真正的提交或回滚当前服务的事务。
5) 当发起方执行完全部业务逻辑的时候,如果无异常会告知lcn协调者,lcn协调者再分别告诉该请求链上的所有参与方可以提交了,再进行真正的提交。若发起方调用完参与方后报错了,也会告知lcn协调者,lcn协调者再告知所有的参与方进行真正的回滚操作,这样就解决了分布式事务的问题。
3.3 LCN本地环境部署
3.3.1 本地部署注册中心
注册中心是微服务中最为重要的一个组件,提供了服务间在调用时需要的一些ip和端口等列表信息。
我这边采用nacos作为注册中心,具体如何一分钟快速搭建本地nacos可参见:https://nacos.io/zh-cn/docs/quick-start.html 注册中心的部署略过不做详细的说明。搭建好nacos后,访问127.0.0.1:8848/nacos进行访问(账号密码默认为nacos),出现如下界面即完成了nacos本地的搭建。
3.3.2 本地部署协调者TM环境
在部署TX-LCN中有遇到问题或想查看部署详细的操作可以访问如下地址:http://www.txlcn.org/zh-cn/docs/start.html 。
1) 我们要去github中先将tx-lcn的源码下载下来,这边我们选择的是5.0.2的版本,github下载源码地址:https://github.com/codingapi/tx-lcn/releases。
2) 创建相应的数据库表,先创建MySQL数据库, 名称为: tx-manager,再创建数据库表t_tx_exception。
CREATE TABLE `t_tx_exception` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `group_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `unit_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `mod_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `transaction_state` tinyint(4) NULL DEFAULT NULL, `registrar` tinyint(4) NULL DEFAULT NULL, `remark` varchar(4096) NULL DEFAULT NULL, `ex_state` tinyint(4) NULL DEFAULT NULL COMMENT '0 未解决 1已解决', `create_time` datetime(0) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
3) 打开我们下好的源码,找到txlcn-tm模块,修改其配置文件application.properties,具体修改的内容根据自己业务而定,官方也有相应配置内容,但需要注意首次运行要将配置文件的这段设置为create:spring.jpa.hibernate.ddl-auto=create。
4) 准备将txlcn-tm模块打包成jar包,先执行打包命令: mvn clean package -Dmaven.test.skip=true ,打包完成后可以在模块中看到出现了jar包。
5) 开始对jar包进行运行,进入到target目录,执行命令启动jar包:java -jar txlcn-tm-5.0.2.RELEASE.jar
6) 运行成功后可以看到tomcat启动成功。
7) 访问控制台的端口为7919,默认密码codingapi。
至此,TM部署并启动完成。
3.3.3 本地配置客户端TC环境
TC就是我们的客户端,也就是我们的微服务,配置相对简单如下:
1) 引入相关依赖,其中一个是lcn核心包,一个是建立长连接的netty包。
<dependency> <groupId>com.codingapi.txlcn</groupId> <artifactId>txlcn-tc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>com.codingapi.txlcn</groupId> <artifactId>txlcn-txmsg-netty</artifactId> <version>5.0.2.RELEASE</version> </dependency>
2) 配置yml文件,设置服务连接到lcn服务。(注意下lcn控制台是7970,这个8070是lcn通讯端口号)
tx-lcn: client: manager-address: 127.0.0.1:8070 logger: enabled: true
3) 引入依赖并配置好yml文件后,在服务的启动类上添加@EnableDistributedTransaction注解,开启分布式事务。
4) 在业务的方法上面添加注解@LcnTransaction和@Transactional,如下图。
5) 做好这些配置后,开始启动项目,启动成功项目后可以在lcn控制台看到相应的服务列表,能够看到则说明服务已经实现了对lcn事务协调者管理器的注册。
看到服务都能够成功注册进TM后,便全部完成TX-LCN本地环境的搭建,后续可以开始测试自己的业务代码是否解决了分布式事务的问题。