精华内容
下载资源
问答
  • LCN分布式事务

    2020-05-12 11:39:03
    分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点上。 例子:电商系统的订单系统和库存系统 图中包含了库存和订单两个独立的微服务,每个微服务维护了...

    一、分布式事务的介绍

    数据库的事务ACID:原子性,隔离性,一致性,持久性。
    事务的隔离级别:脏读,不可重复读,幻读
    在这里插入图片描述

    1.什么是分布式事务?

    分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点上。
    例子:电商系统的订单系统和库存系统
    在这里插入图片描述
    图中包含了库存和订单两个独立的微服务,每个微服务维护了自己的数据库。在交易系统的业务逻辑中,一个商品在下单之前需要先调用库存服务,进行扣除库存,在调用订单服务,创建订单记录。
    在这里插入图片描述
    正常情况下,两个数据库各自更新成功,两边的数据维持着一致性。
    如果在非正常情况下,可能库存的扣减完成,随后的订单记录却因为某些原因插入失败。或者订单创建成功,但是库存扣减的数据量失败,这时两边的数据量失去了应有的一致性。
    在这里插入图片描述
    这时候就绪哟为了保证事务的一致性,单数据源的用单机事务来保证,多数据源就需要依赖分布式事务来处理。

    2. 分布式事务处理的方式

    1)XA的两阶段提交方案

    A) 基本概念

            XA 协议由 OracleTuxedo 首先提出的,并交给 X/Open 组织,作为资源管理器(数据库) 与事务管理器的接口标准。目前,Oracle、Informix、DB2 和 Sybase 等各大数据库厂家都提 供对 XA 的支持。XA 协议采用两阶段提交方式来管理分布式事务。XA 接口提供资源管理 器与事务管理器之间进行通信的标准接口。
            XA 就是 X/OpenDTP 定义的交易中间件与数据库之间的接口规范(即接口函数),交 易中间件用它来通知数据库事务的开始、结束以及提交、回滚等。XA 接口函数由数据库厂 商提供。
            X/Open 组织(即现在的 OpenGroup)定义了分布式事务处理模型。X/Open DTP 模型 (1994)包括应用程序(AP)、事务管理器(TM)、资源管理器(RM)、通信资源管理 器(CRM)四部分。一般常见的事务管理器(TM)是交易中间件,常见的资源管理器(RM) 是数据库,常见的通信资源管理器(CRM)是消息中间件

    B)XA协议的一阶段提交

    在这里插入图片描述
          如果在程序中开启了事务,那么在应用程序发出提交/回滚请求后,数据库执行操作, 而后将成功/失败返回给应用程序,程序继续执行。
          一阶段提交协议相对简单。优点也很直观,它不用再与其他的对象交互,节省了判断 步骤和时间,所以在性能上是在阶段提交协议中最好的。但缺点也很明显:数据库确认执行 事务的时间较长,出问题的可能性就随之增大。如果有多个数据源,一阶段提交协议无法协 调他们之间的关系。
    注意:一阶段提交事务,不能解决分布式事务,只能用于单体架构项目。

    C)XA协议的二阶段提交 (好处:添加了一个管理者的角色)

    在这里插入图片描述
      二阶段协议通过将两层变为三层,增加了中间的管理者角色,从而协调多个数 据源之间的关系,二阶段提交协议分为两个阶段。
    在这里插入图片描述
      应用程序调用了事务管理器的提交方法,此后第一阶段分为两个步骤:
      事务管理器通知参与该事务的各个资源管理器,通知他们开始准备事务。
      资源管理器接收到消息后开始准备阶段,写好事务日志并执行事务,但不提交,然后将 是否就绪的消息返回给事务管理器(此时已经将事务的大部分事情做完,以后的内容耗时极 小)。
    在这里插入图片描述
      第二阶段也分为两个步骤:
      事务管理器在接受各个消息后,开始分析,如果有任意其一失败,则发送回滚命令,否 则发送提交命令。
       各个资源管理器接收到命令后,执行(耗时很少),并将提交消息返回给事务管理器。 事务管理器接受消息后,事务结束,应用程序继续执行。
      为什么要分两步执行?一是因为分两步,就有了事务管理器统一管理的机会;二尽可能晚地提交事务,让事务在提交前尽可能地完成所有能完成的工作,这样,最后的提交阶 段将是耗时极短,耗时极短意味着操作失败的可能性也就降低。
      同时,二阶段提交协议为了保证事务的一致性,不管是事务管理器还是各个资源管 理器,每执行一步操作,都会记录日志,为出现故障后的恢复准备依据。
    缺点:
      1 二阶段提交协议的存在的弊端是阻塞,因为事务管理器要收集各个资源管理器的响应 消息,如果其中一个或多个一直不返回消息,则事务管理器一直等待,应用程序也被阻塞, 甚至可能永久阻塞。
       2 两阶段提交理论的一个广泛工业应用是 XA 协议。目前几乎所有收费的商业数据库都 支持 XA 协议。XA 协议已在业界成熟运行数十年,但目前它在互联网海量流量的应用场景 中,吞吐量这个瓶颈变得十分致命,因此很少被用到

    2)TCC解决方案

    1)TCC介绍(开发量大)

    TCC 是由支付宝架构师提供的一种柔性解决分布式事务解决方案,主要包括三个步骤 (三个接口)
    Try:预留业务资源/数据效验
    Confirm:确认执行业务操作
    Cancel:取消执行业务操作
    在这里插入图片描述

    2) TCC原理

    与XA的区别:事务的管理者是以一个独立的服务出现,而不是中间件
      TCC 方案在电商、金融领域落地较多。TCC 方案其实是两阶段提交的一种改进。其 将整个业务逻辑的每个分支显式的分成了 Try、Confirm、Cancel 三个操作。Try 部分完成 业务的准备工作,confirm 部分完成业务的提交,cancel 部分完成事务的回滚。基本原理如 下图所示。

    在这里插入图片描述

    3)TCC 的关键流程如下图(以创建订单和扣减库存为例子)

    在这里插入图片描述

    4)TCC的优缺点

    **优点:**让应用自己定义数据库操作的粒度,使得降低锁冲突、提高吞吐量称为可能。
    **缺点:**对应用的侵入性强。业务逻辑的每个分支都需要实现 try、confirm、cancel 三个操作,应用侵入性较强,改造成本高。 实现难度较大。需要按照网络状态、系统故障等不同的失败原因实现不同的 回滚策略。为了满足一致性的要求,confirm 和 cancel 接口必须实现幂等(多次操作时只执行一次操作)。

    3)分布式事务中间件解决方案

    分布式事务中间件其本身并不创建事务,而是基于对本地事务的协调从而达到事务一致性 的效果。典型代表有:阿里的 GTS(https://www.aliyun.com/aliware/txc)、开源应用 LCN。

    二、LCN分布式事务处理的框架介绍

    1.LCN(锁定事务单元(lock)、确认事务模块状态(confirm)、通知事务(notify))由来

    用来解决分布式事务。
    特点:不是数据库中间件产品,采用柔性事务机制,需要事务协调器

    2.LCN相关资料

    tx-lcn 官方地址:https://www.txlcn.org/
    tx-lcnGithub 地址: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

    3.LCN框架的原理及执行步骤

    1)LCN原理

    在这里插入图片描述

    2)LCN的执行步骤(重要)

    A) 创建事务组

    事务组:当前所涉及的所有事务的集合
    创建事务组是指在事务发起方开始执行业务代码之前先调用 TxManager 创建事务组 对象,然后拿到事务标示 GroupId 的过程。

    B) 添加事务组

    添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息添加通知给 TxManager 的操作。

    C) 关闭事务组

    是指在发起方执行完业务代码以后,将发起方执行结果状态通知给 TxManager 的动 作。当执行完关闭事务组的方法以后,TxManager 将根据事务组信息来通知相应的参与模 块提交或回滚事务。

    D) 业务执行流程图

    在这里插入图片描述

    4.LCN的事务协调机制

    在这里插入图片描述
    提交的时候是提交给谁呢?
    是提交给了我们的 TxClient 模块。然后 TxCliient 模块下有一个连接池,就是框架自定 义的一个连接池(如图 DB 连接池);这个连接池其实就是在没有通知事务之前一直占有着 这次事务的连接资源,就是没有释放。但是他在切面里面执行了 close 方法。在执行 close 的时候。如果需要(TxManager)分布式事务框架的连接。他被叫做“假关闭”,也就是没有 关闭,只是在执行了一次关闭方法。实际的资源是没有释放的。这个资源是掌握在 LCN 的 连接池里的。
    当 TxManager 通知提交或事务回滚的时候呢?
    TxManager 会通知我们的 TxClient 端。然后 TxClient 会去执行相应的提交或回滚。提交 或回滚之后再去关闭连接。这就是 LCN 的事务协调机制。说白了就是代理 DataSource 的机 制;相当于是拦截了一下连接池,控制了连接池的事务提交。

    5. LCN的事务补偿机制

    1)什么是事务补偿机制

    LCN 的补偿事务原理是模拟上次失败事务的请求,然后传递给 TxClient 模块然后再次 执行该次请求事务。
    简单的说:lcn 事务补偿是指在服务挂机和网络抖动情况下 txManager 无法通知事务单 元时。(通知不到也就两种原因服务挂了和网络出问题)在这种情况下 TxManager 会做一 个标示;然后返回给发起方。告诉他本次事务有存在没有通知到的情况。
    那么如果是接收到这个信息之后呢,发起方就会做一个标示,标示本次事务是需要补偿 事务的。这就是事务补偿机制。

    2) 为什么需要事务补偿

    事务补偿是指在执行某个业务方法时,本应该执行成功的操作却因为服务器挂机或者网 络抖动等问题导致事务没有正常提交,此种场景就需要通过补偿来完成事务,从而达到事务 的一致性(2种情况,实时一致性:当前操作必须保证一致性;最终一致性:未提交成功,但是一段时间之后,在提交只要保证一致即可)。

    3) 补偿机制的触发条件

    当执行关闭事务组步骤时,若发起方接受到失败的状态后将会把该次事务识别为待补偿 事务,然后发起方将该次事务数据异步通知给 TxManager。TxManager 接受到补偿事务以后 先通知补偿回调地址,然后再根据是否开启自动补偿事务状态来补偿或保存该次切面事务数 据

    三、LCN分布式事务框架的应用

    1.案例设计

    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 数据库设计

    创建两个数据库分别为: sxt_orders, sxt_inventory。 springcloud-order 操作 sxt_orders 库, springclooud-inventory 操作 sxt_inventory 库

    2. 创建服务

    2.1 创建项目

    2.1.1 springcloud-portal

    pom文件:
    application.properties配置文件

    2.1.2 springcloud-order

    pom文件:
    application.properties配置文件

    2.1.3 springcloud-inventory

    pom文件:
    application.properties配置文件

    展开全文
  • TX-LCN分布式事务

    2021-01-20 10:56:05
    目录1 分布式事务1.1 分布式事务是什么?1.2 什么时候使用分布式事务1.3 分布式事务常见解决方案1.3.1 基于 XA 协议的两阶段提交1.3.2 消息事务+最终一致性2 分布式事务理论依据2.1 CAP 定理2.1.1 一致性(C)2.1.2 ...

    目录

    1 分布式事务

    1.1 分布式事务是什么?

    • 在分布式系统中,事务参与者在不同的分布式节点上事务操作的数据源不是同一个, 这些情况产生的事务都叫做分布式事务。
    • 例如:
      项目 A 实现 Tb_item 表新增、项目 B 实现 tb_item_param 新增,现在需要实现商品新增,需要把项目 A 和项目 B 两个项目新增的方法组成一个事务,这个事务就是分布式事务。
    • 例如:
      项目中向 MySQL 做新增,同时还需要向 Redis 或 MongoDB 执行新增,希望执行 MySQL 或 Redis 或 MongoDB 时如果出现异常进行事务回滚,这种情况也成为分布式事务。

    1.2 什么时候使用分布式事务

    • 事务的概念最早是在学习数据库(MySQL、Oracle)中接触到的,一个事务(本地事务)就是一系列 SQL 语句的集合,只要在执行过程中一条 SQL 出错就会导致整个事务失败,回滚到原点。而在分布式系统中存在多模块完成一次业务。那么就存在一个业务由多模块操作同一个数据源
      设置
    • 甚至可能存在一个业务横跨多种数据源节点的可能。这些问题都可以由分布式事务解决方案 TX-LCN 解决

    在这里插入图片描述

    1.3 分布式事务常见解决方案

    1.3.1 基于 XA 协议的两阶段提交

    • 分布式事务通常采用 2PC 协议,全称 Two Phase Commitment Protocol。该协议主要为了解决在分布式数据库场景下,所有节点间数据一致性的问题。分布式事务通过 2PC 协议将提交分成两个阶段:
      1.prepare
      2.commit/rollback
    • 阶段一为准备(prepare)阶段。即所有的参与者准备执行事务并锁住需要的资源。参与者 ready 时,向 transaction manager 报告已准备就绪。
    • 阶段二为提交阶段(commit)。当 transaction manager 确认所有参与者都 ready 后,向所有参与者发送 commit 命令

    1.3.2 消息事务+最终一致性

    • 所谓的消息事务就是基于消息中间件的两阶段提交,本质上是对消息中间件的一种特殊利用,它是将本地事务和发消息放在了一个分布式事务里,保证要么本地操作成功并且对外发消息成功,要么两者都失败
    • 分布式事务,本质上是对多个数据库的事务进行统一控制,按照控制力度可以分为:不控制、部分控制和完全控制。不控制就是不引入分布式事务,部分控制就是各种变种的两阶段提交,包括上面提到的消息事务+最终一致性、TCC 模式,而完全控制就是完全实现两阶段提交。部分控制的好处是并发量和性能很好,缺点是数 据一致性减弱了,完全控制则是牺牲了性能,保障了一致性,具体用哪种方式,最终还是取决于业务场景

    2 分布式事务理论依据

    • 分布式事务存在两大理论依据:CAP 定理和 BASE 理论

    2.1 CAP 定理

    • CAP 定理是指在一个分布式系统中 Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性),最多同时满足其中两个,三者不可兼得。

    2.1.1 一致性(C)

    • 在分布式系统中所有节点的状态是一样的。

    2.1.2 可用性(A)

    • 在集群中一部分节点出现故障后,整个集群是否还能响应客户端请求。

    2.1.3 分区容错性(P)

    • 以实际效果而言,分区相当于对操作的时限要求。如果系统不能在一定时限内达到数据一致性,就意味着发生了分区的情况,此时就必须在 A 和 C 中做选择。

    2.2 BASE 理论

    • 是指 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的缩写。
    • BASE 理论是对 CAP 中一致性和可用性权衡的结果,是基于 CAP 演化而来的。
    • BASE 理论核心思想:即使无法做到强一致性,每个应用都可以根据自身业务特点,采用适当的方式达到最终一致性

    2.2.1 基本可用(BA)

    • 是指在分布式系统中出现不可知故障的时候,允许损失部分可用性。此处要注意:损失部分可用性,不代表整个系统不可用
    • 例如:
      1.可以增加响应时间。由之前 0.5 秒,在出现故障的时候变成 1~2 秒
      2.由于一些特殊原因,使网站访问流量激增,为了保证整个系统的稳定性,部分访问者可能被引导到降级页面中

    2.2.2 软状态(S)

    • 是指系统中数据允许存在中间状态(软状态),并认为这个状态是不影响系统的可用性的。通俗解释:允许分布式节点之间存在同步延迟
    • 例如:
      在 Eureka 集群中数据同步时就存在软状态

    2.2.3 最终一致性

    • 允许整个系统中数据在经过一定时间后,最终能达到整个系统的一致性。但是这个时间绝对不可以过长
    • 强一致性要求系统接收请求后,整个系统必须达到一致性效果,才会响应结果
    • 最终一致性是弱一致性的特例。满足最终一致性的系统在响应给用户结果时整个系统可能是没有达到一致性的,但是最终一定会达到一致性效果的

    3 TX-LCN 概述

    3.1 简介

    • LCN 框架在 2017 年 6 月发布第一个版本
    • LCN 早期设计时,1.0 版本和 2.0 版本设计步骤如下:
      1.锁定事务单元(Lock)
      2.确认事务模块状态(Confirm)
      3.通知事务(Notify)
    • 取各自首字母后名称为 LCN。
    • LCN 框架从 5.0 开始兼容了 LCN、TCC、TXC 三种事务模式,为了和 LCN 框架区分,从 5.0 开始把 LCN 框架更名为:TX-LCN 分布式事务框架

    3.2 TX-LCN 原理

    • TX-LCN 由两大模块组成,TxClient、TxManager
    • TxClient 作为模块的依赖框架,提供了 TX-LCN 的标准支持,事务发起方和参与方都属于 TxClient。TxManager 作为分布式事务的控制方,控制整个事务

    在这里插入图片描述

    3.2.1 原理中核心内容

    3.2.1.1 创建事务组

    • 是指在事务发起方开始执行业务代码之前先调用 TxManager 创建事务组对象,然后拿到事务标识 GroupId 的过程。

    3.2.1.2 加入事务组

    • 添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息通知给 TxManager 的操作。

    3.2.1.3 通知事务组

    • 是指在发起方执行完业务代码以后,将发起方执行结果状态通知给 TxManager,TxManager 将根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方

    4 TX-LCN 事务模式

    • Tx-LCN 5.0 开始支持三种事务模式,分别是:LCN、TCC、TXC 模式。
    • 每种模式在实际使用时有着自己对应的注解。
    LCN:@LcnTransaction 
    TCC:@TccTransaction 
    TXC:@TxcTransaction
    123
    

    4.1 LCN 模式

    4.1.1 原理介绍

    • LCN 模式是通过代理 JDBC 中 Connection 的方式实现对本地事务的操作,然后在由TxManager 统一协调控制事务。当本地事务提交回滚或者关闭连接时将会执行假操作,该代 理的连接将由 LCN 连接池管理

    4.1.2 模式特点

    • 该模式对代码的嵌入性低。
    • 该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。
    • 该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障
    • 该模式缺陷在于代理的连接需要随事务发起方一同释放连接,增加了连接占用的时间
    • 总结:LCN 模式适合能用 JDBC 连接的所有支持事务的数据库

    4.2 TCC 事务模式

    4.2.1 原理介绍

    • TCC 事务机制相对于传统事务机制(X/Open XA Two-Phase-Commit),其特征在于它不依赖资源管理器(RM)对 XA 的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。主要由三步操作,Try: 尝试执行业务、 Confirm:确认执行业务、 Cancel: 取消执行业务

    4.2.2 代码说明

    • 每个 TCC 事务处理方法可以额外包含 confirmXxx 和 cancelXxx 的方法(),出现失败问题,需要在 cancel 中通过业务逻辑把改变的数据还原回来
    • confirmXxx 和 cancelXxx 两个方法会由 TxManager 进行统一协调调用
    • confirmXxx 和 cancelXxx 也可以在@TccTransaction 注解中通过属性明确指定
    @TccTransaction 
    public String demo(){ 
    	// 正常的 service 方法,也是 Try 尝试执行执行 
    }
    
    public void confirmDemo(){ 
    	// 当 demo 方法没有出现异常时执行的方法 
    	// 方法名称必须叫做 confirm+代理方法首字母 
    }
    
    public void cancelDemo(){ 
    	// 当 demo 方法出现异常时执行的方法 
    	// 方法名称必须叫做 cancel+代理方法首字母 
    }
    

    4.2.3 模式特点

    • 该模式对代码的嵌入性高,要求每个业务需要写二个以上步骤的操作
    • 该模式对有无本地事务控制都可以支持,使用面更广
    • 数据一致性控制几乎完全由开发者控制,对业务开发难度要求高
    • 总结:Tcc 模式应用于所有不支持 XA 事务的软件。例如:redis,mongodb 等

    4.3 TXC 事务模式

    4.3.1 原理介绍

    • TXC 模式命名来源于淘宝,实现原理是在执行 SQL 之前,先查询 SQL 的影响数据,然后保存执行的 SQL 信息和创建锁。当需要回滚的时候就采用这些记录数据回滚数据库,目前锁实现依赖 redis 分布式锁控制。(在使用 lcn 时必须要配置 redis 参数)

    4.3.2 模式特点

    • 该模式同样对代码的嵌入性低
    • 该模式仅限于对支持 SQL 方式的模块支持
    • 该模式由于每次执行 SQL 之前需要先查询影响数据,因此相比 LCN 模式消耗资源与时间要多
    • 该模式不会占用数据库的连接资源
    • 总结:只能用在支持 SQL 的数据库。对资源消耗较多。建议使用 LCN 模式

    5 XA 的两阶段提交方案(数据库支持分布式事务,为什么还用 TX-LCN)

    5.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)是消息中间件。

    5.2 XA 协议的一阶段提交

    在这里插入图片描述

    • 如果在程序中开启了事务,那么在应用程序发出提交/回滚请求后,数据库执行操作,而后将成功/失败返回给应用程序,程序继续执行
    • 一阶段提交协议相对简单。优点也很直观,它不用再与其他的对象交互,节省了判断步骤和时间,所以在性能上是在阶段提交协议中最好的。但缺点也很明显:数据库确认执行事务的时间较长,出问题的可能性就随之增大。如果有多个数据源,一阶段提交协议无法协调他们之间的关系

    5.3 XA 协议的二阶段提交

    • 在一阶段协议的基础上,有了二阶段协议,二阶段协议的好处是添加了一个管理者角色
      在这里插入图片描述

    • 很明显,二阶段协议通过将两层变为三层,增加了中间的管理者角色,从而协调多个数据源之间的关系,二阶段提交协议分为两个阶段
      在这里插入图片描述

      应用程序调用了事务管理器的提交方法,此后第一阶段分为两个步骤:

    • 事务管理器通知参与该事务的各个资源管理器,通知他们开始准备事务。

    • 资源管理器接收到消息后开始准备阶段,写好事务日志并执行事务,但不提交,然后将是否就绪的消息返回给事务管理器(此时已经将事务的大部分事情做完,以后的内容耗时极小)。
      在这里插入图片描述

      第二阶段也分为两个步骤:

    • 事务管理器在接受各个消息后,开始分析,如果有任意其一失败,则发送回滚命令,否则发送提交命令

    • 各个资源管理器接收到命令后,执行(耗时很少),并将提交消息返回给事务管理器

    • 事务管理器接受消息后,事务结束,应用程序继续执行

    • 为什么要分两步执行?一是因为分两步,就有了事务管理器统一管理的机会;二尽可能晚地提交事务,让事务在提交前尽可能地完成所有能完成的工作,这样,最后的提交阶段将是耗时极短,耗时极短意味着操作失败的可能性也就降低

    • 同时,二阶段提交协议为了保证事务的一致性,不管是事务管理器还是各个资源管理器,每执行一步操作,都会记录日志,为出现故障后的恢复准备依据


    缺点:

    • 二阶段提交协议的存在的弊端是阻塞,因为事务管理器要收集各个资源管理器的响应消息,如果其中一个或多个一直不返回消息,则事务管理器一直等待,应用程序也被阻塞,甚至可能永久阻塞
    • 两阶段提交理论的一个广泛工业应用是 XA 协议。目前几乎所有收费的商业数据库都支持 XA 协议。XA 协议已在业界成熟运行数十年,但目前它在互联网海量流量的应用场景中,吞吐量这个瓶颈变得十分致命,因此很少被用到

    6 TxManager 搭建

    6.1 创建项目,添加依赖

    • 新建项目 TxManager,并添加依赖
    • 依赖包含了 Spring-boot 的依赖,版本是 2.0.5,如果希望把版本改变成 2.2.2 或其他版本只需要添加 spring-boot-starter-parent 继承即可
    <dependencies> 
    	<dependency> 
    		<groupId>com.codingapi.txlcn</groupId> 
    		<artifactId>txlcn-tm</artifactId> 
    		<version>5.0.2.RELEASE</version> 
    	</dependency> 
    </dependencies>
    

    6.2 执行 SQL 文件

    • 执行 tx-manager.sql 文件(在任意的数据库下执行即可)
    • tx-manager.sql 在 txlcn-tm-5.0.2.RELEASE.zip 压缩包中。
    • 在 MySQL 生成 tx-manager 的数据库,在数据库中新建 t_tx_exception 的表,此表用作存储事务组信息
    • 注意:
    • 默认情况下 tx-manager 需要记录日志信息的,需要在项目中配置日志连接数据库相关参数,其中日志存储数据库没有要求,可以存储到任意数据库中,当运行后会自动在数据库中生成一个日志表。如果不希望记录日志可以直接设置 tx-lcn.logger.enabled=false,关闭日志功能,下面的日志连接数据库参数也可以不用配置。
    • 在实际案例演示中会把所有的日志记录功能关闭。如果希望记录记录日志需要把下面代码在所有引用 tx-lcn 的项目的配置文件中进行配置
    tx-lcn.logger.enabled=true 
    tx-lcn.logger.driver-class-name=com.mysql.jdbc.Driver 
    tx-lcn.logger.jdbc-url=jdbc:mysql://192.168.8.131:3306/tx-manager?characterEncoding=UTF-8 
    tx-lcn.logger.username=root 
    tx-lcn.logger.password=root
    

    6.3 配置配置文件

    • 在 TxManager 项目的 resource 下新建 application.properties。tx-lcn 在当前版本有个 bug 只能使用 properties 文件,使用 yml 文件会导致配置文件无法被加载的问题
    • 配置文件中内容上半部分是 tx-manager 数据库的连接信息。中间包含 redis 连接信息(此处连接的是 redis 单机版,端口默认,没有密码),下面是关闭日志记录功能
    • 小提示:
    • 依赖 Redis,所以需要安装 Redis。
    • 7970 是客户端访问端口,是 Txmanager 可视化界面访问端口,此端口任意。
    spring.application.name=TransactionManager
    server.port=7970
    
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/tx-manager?characterEncoding=UTF-8
    spring.datasource.username=root
    spring.datasource.password=root
    spring.redis.host=192.168.8.129
    
    tx-lcn.logger.enabled=false
    

    6.4 新建启动类

    • 注意注解@EnableTrasactionManagerServer 必须有
    @SpringBootApplication 
    @EnableTransactionManagerServer
    public class MyApplication { 
    	public static void main(String[] args) { 
    		SpringApplication.run(MyApplication.class,args); 
    	} 
    }
    

    6.5 访问管理界面

    • 在浏览器输入:http://localhost:7970 访问。
    • 密码默认是 codingapi
    • 可以在配置文件中修改登录密码
    tx-lcn.manager.admin-key=dqcgm
    

    在这里插入图片描述

    7 LCN 事务模式

    7.1 创建数据库表

    • 注意:不要给 student 表添加外键约束。如果添加会导致分布式事务执行时 student 新增失败,因为 teacher 没有提交时 student 的 tid 值无法获取。
      设置

    7.2 创建项目

    • 案例使用聚合项目进行演示。
    • 创建父项目,名称为 LcnParent

    7.2.1 配置 pom.xml

    • txlcn-tc 是 TX-LCN 的客户端包
    • txlcn-txmsg-netty 是 LCN 客户端连接 TxManager 需要的包
    <parent>
    	<groupId>org.springframework.boot</groupId> 
    	<artifactId>spring-boot-starter-parent</artifactId> 
    	<version>2.2.6.RELEASE</version>
    </parent>
    <dependencies>
    	<dependency> 
    		<groupId>org.springframework.boot</groupId> 
    		<artifactId>spring-boot-starter-web</artifactId> 
    	</dependency>
    	<dependency> 
    		<groupId>org.mybatis.spring.boot</groupId> 
    		<artifactId>mybatis-spring-boot-starter</artifactId> 
    		<version>2.1.2</version> 
    	</dependency>
    	<dependency> 
    		<groupId>org.springframework.cloud</groupId> 
    		<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 
    	</dependency>
    	<dependency> 
    		<groupId>org.springframework.cloud</groupId> 
    		<artifactId>spring-cloud-starter-openfeign</artifactId> 
    	</dependency>
    
    	<dependency> 
    		<groupId>mysql</groupId> 
    		<artifactId>mysql-connector-java</artifactId> 
    		<version>5.1.48</version> 
    		<scope>runtime</scope> 
    	</dependency>
    	<dependency> 
    		<groupId>org.projectlombok</groupId> 
    		<artifactId>lombok</artifactId> 
    		<optional>true</optional> 
    	</dependency>
    	<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>
    </dependencies>
    <dependencyManagement>
    	<dependencies> 
    		<dependency> 
    			<groupId>org.springframework.cloud</groupId> 
    			<artifactId>spring-cloud-dependencies</artifactId> 
    			<version>Hoxton.SR4</version> 
    			<type>pom</type> 
    			<scope>import</scope> 
    		</dependency> 
    	</dependencies> 
    </dependencyManagement>
    

    7.3 新建 pojo 项目

    • 把实体类提出来
    • 新建两个实体类。
    • 新建 com.dqcgm.pojo.Teacher
    @Data 
    public class Teacher { 
    	private Long id; 
    	private String name; 
    }
    12345
    
    • 新建 com.dqcgm.pojo.Student
    @Data 
    public class Student { 
    	private Long id; 
    	private String name; 
    	private Long tid; 
    }
    

    7.4 创建项目 teacher_insert

    • 新建 teacher_insert 项目

    7.4.1 配置 pom.xml

    • 依赖 pojo
    <dependencies> 
    	<dependency> 
    		<artifactId>pojo</artifactId> 
    		<groupId>com.dqcgm</groupId> 
    		<version>0.0.1-SNAPSHOT</version> 
    	</dependency> 
    </dependencies>
    

    7.4.2 编写配置文件

    • 新建 application.yml.
    • 数据源连接的是 Teacher 表所在数据库
    • eureka 单机版可以省略。
    • manager-address 配置 TxManager 项目的 ip 及端口。端口是内部访问端口,而不是可视化页面的端口
    spring: 
    	datasource:
    		url: jdbc:mysql://localhost:3306/microservice
    		driver-class-name: com.mysql.jdbc.Driver
    		username: root
    		password: root
    	application:
    		name: teacher-insert
    server: 
    	port: 8080
    
    eureka: 
    	client: 
    		service-url: 
    			defaultZone: http://localhost:8761/eureka/
    
    tx-lcn:
    	client: 
    		manager-address: 127.0.0.1:8070
    

    7.4.3 新建 mapper

    • 新建 com.dqcgm.mapper.TeacherMapper
    @Mapper 
    public interface TeacherMapper { 
    	@Insert("insert into teacher values(#{id},#{name})") 
    	int insert(Teacher teacher); 
    }
    12345
    

    7.4.4 新建 service 及实现类

    • 新建 com.dqcgm.service.TeacherService 及实现类。
    • 方法上@Transactional 一定要有。本地事务控制。
    • @LcnTransaction 表示当前方法加入到分布式事务控制。
    • @LcnTransaction 属性 propagation 可取值
      1.DTXPropagation.REQUIRED:默认值,表示如果当前没有事务组创建事务组,如果有事务组,加入事务组。多用在事务发起方。
      DTXPropagation.SUPPORTS:如果当前没有事务组以本地事务运行,如果当前有事务组加入事务组。多用在事务参与方法。
    public interface TeacherService { 
    	int insert(Teacher teacher); 
    }
    123
    @Service 
    public class TeacherServiceImpl implements TeacherService {
    	@Autowired
    	private TeacherMapper teacherMapper;
    
    	@Override 
    	@LcnTransaction 
    	@Transactional
    	public int insert(Teacher teacher) { 
    		return teacherMapper.insert(teacher); 
    	} 
    }
    

    7.4.5 新建控制器

    • 新建 com.dqcgm.controller.TeacherController。
    • 由于在 student_insert 中通过 OpenFeign 进行条件,参数使用请求体数据,所以控制器方法的参数需要添加@RequestBody
    @Controller 
    public class TeacherController {
    	@Autowired 
    	private TeacherService teacherService;
    
    	@RequestMapping("/insert") 
    	@ResponseBody
    	public int insert(@RequestBody Teacher teacher){ 
    		System.out.println("taecher"+teacher); 
    		return teacherService.insert(teacher); 
    	} 
    }
    

    7.4.6 新建启动器

    • 新建 com.dqcgm.TeacherInsertApplication。
    • 一定要有注解@EnableDistributedTransaction 表示启动分布式事务
    @SpringBootApplication 
    @EnableDistributedTransaction 
    public class TeacherInsertApplication {
    	public static void main(String[] args) {
    		SpringApplication.run(TeacherInsertApplication.class,args); 
    	} 
    }
    

    7.5 新建项目 student_insert

    7.5.1 编写 pom.xml

    • 添加对 pojo 依赖
    <dependencies> 
    	<dependency> 
    		<artifactId>pojo</artifactId> 
    		<groupId>com.dqcgm</groupId> 
    		<version>0.0.1-SNAPSHOT</version> 
    	</dependency> 
    </dependencies>
    

    7.5.2 创建配置文件

    • 新建 application.yml
    spring: 
    	datasource:
    		url: jdbc:mysql://localhost:3306/microservice 
    		driver-class-name: com.mysql.jdbc.Driver 
    		username: root 
    		password: root
    	application:
    		name: student-insert
    server: 
    	port: 8081
    
    eureka: 
    	client: 
    		service-url: 
    			defaultZone: http://localhost:8761/eureka/
    
    tx-lcn: 
    	client:
    		manager-address: 127.0.0.1:8070
    

    7.5.3 新建 Feign 接口

    • 新建 com.dqcgm.feign.TeacherInsertFeign
    @FeignClient("teacher-insert") 
    public interface TeacherInsertFeign { 
    	@RequestMapping("/insert") int insert(Teacher teacher); 
    }
    

    7.5.4 新建 Mapper

    • 新建 com.dqcgm.mapper.StudentMapper
    @Mapper 
    public interface StudentMapper { 
    	@Insert("insert into student values(#{id},#{name},#{tid})") 
    	int insert(Student student); 
    }
    

    7.5.5 新建 service 及实现类

    • 新建 com.dqcgm.service.StudentService
    • 实现类中对 Teacher 和 Student 的主键都是随机数,为了测试时多次测试方便,所以没有给固定值
    • Student 的姓名通过客户端请求参数传递的,其他都不需要通过参数设置
    public interface StudentService { 
    	int insert(Student student); 
    }
    @Service 
    public class StudentServiceImpl implements StudentService {
    	@Autowired
    	private StudentMapper studentMapper;
    
    	@Autowired 
    	private TeacherInsertFeign teacherInsertFeign;
    
    	@Override 
    	@LcnTransaction 
    	@Transactional 
    	public int insert(Student student) {
    		Teacher teacher = new Teacher();
    		Random random = new Random();
    		teacher.setId((long)random.nextInt(10000));
    		teacher.setName("随意的名称");
    		student.setTid(teacher.getId());
    		student.setId((long)random.nextInt(10000));
    		teacherInsertFeign.insert(teacher);
    		return studentMapper.insert(student);
    	}
    }
    

    7.5.6 新建控制器

    • 新建 com.dqcgm.controller.StudentController
    @Controller 
    public class StudentController {
    	@Autowired 
    	private StudentService studentService;
    	
    	@RequestMapping("/insert") 
    	@ResponseBody 
    	public int insert(Student student){ 
    		return studentService.insert(student); 
    	} 
    }
    

    7.5.7 新建启动类

    • 新建 com.dqcgm.StudentInsertApplication
    @SpringBootApplication 
    @EnableDistributedTransaction 
    @EnableFeignClients 
    public class StudentInsertApplication {
    	public static void main(String[] args) {
    		SpringApplication.run(StudentInsertApplication.class,args);
    	}
    }
    

    7.6 测试结果

    • 在浏览器中输入 http://localhost:8081/insert?name=dqcgm后,如果页面显示 1 并且数据库 teacher 表和 student 表各增加一条数据表示新增成功。
    • 为了测试分布式事务效果,在 student_insert 项目实现类方法 return 上面添加 int i =5/0; 的算术异常,再次访问 url 页面会报 500,并且数据库中没有新增数据,说明分布式事务成功了。
      在这里插入图片描述

    8 TCC 事务模式(多模式混合使用)

    • 在上面 LCN 事务模式代码基础上进行修改

    8.1 新建项目 mongodb_insert

    8.1.1 修改 pom.xml

    • 在当前项目中引入 mongodb 的依赖。如果在父项目中进行引入,则所有的子项目都需要配置 mongodb 的相关属性
    <dependencies> 
    	<dependency> 
    		<groupId>org.springframework.boot</groupId> 
    		<artifactId>spring-boot-starter-data-mongodb</artifactId> 
    		<version>2.2.6.RELEASE</version> 
    	</dependency> 
    </dependencies>
    

    8.1.2 新建配置文件

    • 新建 application.yml。
    • 虽然当前项目是连接 Mongodb 但是也需要配置 MySQL 数据源,因为 LCN 需要在 MySQL 中记录异常信息等
    • 配置文件中比别的项目多了 MongoDB 的配置
    spring: 
    	datasource:
    		url: jdbc:mysql://localhost:3306/microservice
    		driver-class-name: com.mysql.jdbc.Driver
    		username: root
    		password: root
    	application:
    		name: mongodb-insert
    	data:
    		mongodb:
    			authentication-database: admin
    			username: dqcgm
    			password: dqcgmpwd
    			database: lcn
    			host: 192.168.8.139
    			port: 27017
    server:
    	port: 8082
    
    eureka: 
    	client: 
    		service-url: 
    			defaultZone: http://localhost:8761/eureka/
    
    tx-lcn: 
    	client: 
    		manager-address: 127.0.0.1:8070
    

    8.1.3 新建实体类

    • 新建 com.dqcgm.pojo.People
    @Data 
    public class People { 
    	private String id; 
    	private String name; 
    }
    

    8.1.4 新建 service 及实现类

    • 新建 com.dqcgm.service.PeopleService 及实现类
    • 具有@TccTransaction 注解的方法有以下特性
      1.可以没有@Transactional
      2.如果整个分布式事务所有方法执行都没有异常,会回调名称为:confirm+方法名首字母大写的方法。insert 方法的回调方法叫做 confirmInsert()。同时方法参数也可以传递给回调方法
      3.只要整个分布式事务中有一个方法出现异常,会回调 cancel+方法名首字母大写的回调方法。需要在这个方法中编写事务回滚的业务。
    • @TccTransaction 注解属性说明:
      cancelMethod:明确指定失败的回调方法名
      confirmMethod:明确指定成功的回调方法名
    public interface PeopleService { 
    	int insert(People people); 
    }
    @Service 
    public class PeopleServiceImpl implements PeopleService {
    	@Autowired 
    	private MongoTemplate mongoTemplate;
    	@Override 
    	@TccTransaction
    	public int insert(People people) {
    		People result = mongoTemplate.insert(people);
    		if(result!=null){
    			return 1;
    		}
    		return 0;
    	}
    
    	public void cancelInsert(People people){ 
    		System.out.println("执行了 cancel 方法"+people); 
    		mongoTemplate.remove(people); 
    		System.out.println("所谓的事务回滚就是删除新增的数据"); 
    	}
    
    	public void confirmInsert(People people){ 
    		System.out.println("执行了 confirm 方法"+people); 
    	}
    }
    

    8.1.5 新建控制器

    • 新建 com.dqcgm.controller.PeopleController
    @Controller
    public class PeopleController {
    	@Autowired 
    	private PeopleService peopleService; 
    	@RequestMapping("/insert") 
    	@ResponseBody
    	public int insert(People people){ 
    		return peopleService.insert(people); 
    	} 
    }
    

    8.1.6 测试

    • 在浏览器输入 http://localhost:8082/insert?name=dqcgm观察 mongodb 中是否出现了 lcn 的 数据库,数据库中是否出现 People 的集合,people 集合中 name 属性值为 dqcgm

    8.2 修改 student_insert

    8.2.1 新建 feign 接口

    • 新建 com.dqcgm.feign.MongodbInsertFeign。
    • 为了传递普通表单数据,insert 方法参数由@RequestParam。mongodb_insert 控制器方法参数就可以使用 String name 或 People 进行接收
    @FeignClient("mongodb-insert") 
    public interface MongodbInsertFeign { 
    	@RequestMapping("/insert") 
    	int insert(@RequestParam String name); 
    }
    

    8.2.2 修改 service 实现类

    • 修改 com.dqcgm.service.impl.StudentServiceImpl。
    • 在实现类中调用 feign 接口的方法
    • 当前方法依然使用 LCN 事务模式。方法上面加什么事务模式注解只考虑当前方法本地事务,不考虑调用远程方法的事务。如果当前方法中没有本地事务,全是调用远程方法,那么当前方法使用 LCN 或 TCC 事务模式都可以,但是必须要有事务模式,因为如果没有注解就不会想 TxManager 中创建事务组。
    @Service 
    public class StudentServiceImpl implements StudentService {
    	@Autowired 
    	private StudentMapper studentMapper; 
    	@Autowired 
    	private TeacherInsertFeign teacherInsertFeign;
    	@Autowired 
    	private MongodbInsertFeign mongodbInsertFeign; 
    	@Override 
    	@LcnTransaction 
    	@Transactional 
    	public int insert(Student student) {
    		Teacher teacher = new Teacher();
    		Random random = new Random();
    		teacher.setId((long)random.nextInt(10000));
    		teacher.setName("随意的名称");
    		student.setTid(teacher.getId());
    		student.setId((long)random.nextInt(10000));
    		teacherInsertFeign.insert(teacher);
    		mongodbInsertFeign.insert("随意的名称");
    		return studentMapper.insert(student);
    	}
    }
    
    展开全文
  • LCN分布式事务框架

    2020-04-23 12:34:00
    LCN是国产开源的分布式事务处理框架。LCN即:lock(锁定事务单元)、confirm(确认事务模块状态)、notify(通知事务)。 官网:http://www.txlcn.org/zh-cn/ 2.首先介绍3.0与4.0之前的差异 2.1.地址 2.2.添加升级...

    1.LCN是什么

    LCN是国产开源的分布式事务处理框架。LCN即:lock(锁定事务单元)、confirm(确认事务模块状态)、notify(通知事务)。

    官网:http://www.txlcn.org/zh-cn/
    在这里插入图片描述

    2.首先介绍3.0与4.0之前的差异

    2.1.地址

    在这里插入图片描述

    2.2.添加升级如下功能

    在这里插入图片描述
    (1)3.0虽然有事务补偿机制,但4.0在此基础上不仅添加事务补偿机制的策性,还添加了管理的后台可以看到补偿的数据;同时也添加了一个回调地址,可以在补偿之前可以最先知道这次补偿的数据,也可以为我们的框架使用者提供一个决策权。

    (2)同4.0时添加的插件扩展机制,也就是说他更加开放了,他可以可以容纳更多的rpc框架,也可以更多的支持db框架,比如mongodb、redis,还有将来一些框架,如ES等等。

    3.LCN4.0原理

    3.1.架构介绍

    在这里插入图片描述
    有图可得,lcn是通过nginx作为负载均衡的转发,也就是作为Txmanager的负载均衡的一个转发服务器;然后再是我们的TxManager,也就是事务管理器,然后事务管理器依赖两个服务,一个是redis服务,一个是Eureka服务集群;Eureka集群是用于我们TxManager之间的相互服务发现。redis是用于存放我们事务组的信息以及补偿的信息。然后模块A与模块B他们都需要去配置上我们TxClient的包架构(代码的包架构);来支持我们的LCN框架,以及他们的数据库。

    3.2.核心步骤(LCN核心的三步骤)

    首先讲解下什么是事务组:事务组是指的我们在整个事务过程中把各个节点(微服务)单元的事务信息存储在一个固定单元里。但这个信息并不是代表是事务信息,而是只是作为一个模块的标示信息。

    其次介绍事务发起者与参与者
    在这里插入图片描述
    如图,在一次事务中发起的就叫做启动者或者是发起方。然后其他的微服务框架都叫做事务的参与者。

    创建事务组
    是指在事务发起方代码开始执行业务之前先调用TxManager创建事务组对象,然后拿到事务标示GroupId的过程。(这里的groupId表示的是一次事务的唯一标示,就是指我们一次事务中,会有一个groupId存在。其次这里讲述在开始执行业务之前,是因为框架是基于切面的思想,那么切面就切面到了这个方法的业务执行过程中,然后又一个around切面。再然后在开始之前,在业务没有调用业务之前会先调用TxManager去创建事务组,然后创建完事务组以后Txmanager会返回事务组信息,事务组信息中就包含GroupId这个参数,这也就是作为的创建事务组。创建完事务组之后就相当于已经产生一个事务组标示。作为一个串联过程,识别为同一次事务的一个过程)

    添加事务组
    添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息添加通知给TxManager的操作。

    关闭事务组
    是指在发起方执行完业务代码以后(执行到这个地方就表明没有错误和异常,也就是执行结束了,否则并不会执行到这个地方),将发起方执行结果状态通知给TxManager的动作。当执行完关闭事务组的方法以后,TxManager将根据事务组信息来通知相应的参与模块提交或回滚事务。

    LCN正常执行序列图(来源于官方):
    在这里插入图片描述
    LCN异常执行序列图(来源于官方):
    在这里插入图片描述

    3.3.事务协调机制

    在这里插入图片描述
    如图:假设服务已经执行到关闭事务组的过程,那么接下来作为一个模块执行通知给TxManager,然后告诉他本次事务已经完成。那么如图中Txmanager下一个动作就是通过事务组的id,然后获取到本次事务组的事务信息;然后查看一下对应有那几个模块参与,然后如果是有A/B/C三个模块;那么对应的对三个模块做通知、提交、回滚。

    那么提交的时候是提交给谁呢?

    是提交给了我们的TxClient模块。然后TxCliient模块下有一个连接池,就是框架自定义的一个连接池(如图DB连接池);这个连接池其实就是在没有通知事务之前一直占有着这次事务的连接资源,就是没有释放。但是他在切面里面执行了close方法。在执行close的时候。如果需要(TxManager)分布式事务框架的连接。他被叫做“假关闭”,也就是没有关闭,只是在执行了一次关闭方法。实际的资源是没有释放的。这个资源是掌握在LCN的连接池里的。

    然后当TxManager通知提交或事务回滚的时候呢?

    TxManager会通知我们的TxClient端。然后TxClient会去执行相应的提交或回滚。提交或回滚之后再去关闭连接,然后在返回给DB连接池。这就只事务的协调机制。说白了就是代理DataSource的机制;相当于是拦截了一下连接池,控制了连接池的事务提交。

    LCN事务控制原理是由事务模块TxClient下的代理连接池与TxManager的协调配合完成的事务协调控制。

    TxClient的代理连接池实现了javax.sql.DataSource接口,并重写了close方法,事务模块在提交关闭以后TxClient连接池将执行"假关闭"操作,等待TxManager协调完成事务以后在关闭连接。

    3.4.对于代理连接池的优化

    1.自动超时机制
    任何通讯都有最大超时限制,参与模块在等待通知的状态下也有最大超时限制,当超过时间限制以后事务模块将先确认事务状态,然后再决定执行提交或者回滚操作,主要为了给最大资源占用时间加上限制。
    
    2.智能识别创建不同的连接 对于只读操作、非事务操作LCN将不开启代理功能,返回本地连接对象,对于补偿事务的启动方将开启回滚连接对象,执行完业务以后马上回滚事务。
    
    3.LCN连接重用机制 当模块在同一次事务下被重复执行时,连接资源会被重用,提高连接的使用率。
    

    4.Spring Cloud 整合LCN

    4.1.下载LCN工程;

    在LCN的github下载:https://github.com/codingapi/tx-lcn/

    4.2.配置tx-manager事务协调器

    修改其属性文件: (修改下载事务协调服务器的端口、接入的服务注册中心、使用的redis库等的集群或单点配置)

    #######################################txmanager-start#################################################
    #服务端口
    server.port=8899
    
    #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://eurekaserver1:8081/eureka/,http://eurekaserver2:8082/eureka/,http://eurekaserver3:8083/eureka/
    eureka.instance.prefer-ip-address=true
    
    #######################################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.6.211
    #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 int com.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
    

    4.3.启动事务协调者

    启动事务协调者,让事务协调者注入进入eureka;(注意,配置中的redis等必须正常启动)
    启动成功后,检查tx-manager协调者,见下图:
    在这里插入图片描述

    4.4 事务参与方配置

    假定:事务参与方已经是正常运行的服务提供者。样例中的数据库是mysql,连接池采用druid;

    4.4.1 pom文件引入LCN db插件和springcloud支持:
        <properties>
            <lcn.last.version>4.1.0</lcn.last.version>
        </properties>
    
    
            <!-- 引入LCN-->
            <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>
    
    4.4.2 yml添加配置
    tm:
      manager:
        url: http://127.0.0.1:8899/tx/manager/
    
    4.4.3 添加TxManagerTxUrlService到spring中
    package com.mark.springcloud.service.impl;
    
    import com.codingapi.tx.config.service.TxManagerTxUrlService;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Service;
    
    /**
     * 添加从注册中心获取url;注意通过注解放入容器。
     */
    @Service
    public class TxManagerTxUrlServiceImpl implements TxManagerTxUrlService{
        @Value("${tm.manager.url}")
        private String url;
        @Override
        public String getTxUrl() {
            return url;
        }
    }
    

    4.4.4 事务参与方服务:

    package com.mark.springcloud.service.impl;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.codingapi.tx.annotation.ITxTransaction;
    import com.mark.springcloud.dao.DeptDao;
    import com.mark.springcloud.entities.Dept;
    import com.mark.springcloud.service.DeptService;
    /**
     * 注意需要实现 ITxTransaction;
     */
    @Service
    public class DeptServiceImpl implements DeptService, ITxTransaction {
        @Autowired
        private DeptDao dao;
    
        //注意需要开启事务
        @Override
        @Transactional
        public boolean add(Dept dept) {
            boolean rtnValue = dao.addDept(dept);
            return rtnValue;
        }
    }
    
    4.4.5 启动事务参与方

    启动spring boot应用。

    4.5 事务发起方配置

    正常情况下,一个服务一般即可能是事务的发起方也是事务的参与方。(在测试事务发起方、参与方都是同样配置。所以直接略过,只描述发起方特有代码)

    4.5.1 参照样例,实现TxManagerHttpRequestService
    package com.mark.springcloud.controller;
    
    import com.codingapi.tx.netty.service.TxManagerHttpRequestService;
    import com.lorne.core.framework.utils.http.HttpUtils;
    import org.springframework.stereotype.Service;
    
    /**
     * 常见TxManagerHttpRequestService重写get、post方法;
     */
    
    @Service
    public class TxManagerHttpRequestServiceImpl implements TxManagerHttpRequestService{
    
        @Override
        public String httpGet(String url) {
            System.out.println("httpGet-start");
            String res = HttpUtils.get(url);
            System.out.println("httpGet-end");
            return res;
        }
    
        @Override
        public String httpPost(String url, String params) {
            System.out.println("httpPost-start");
            String res = HttpUtils.post(url,params);
            System.out.println("httpPost-end");
            return res;
        }
    }
    
    4.5.2 事务发起方服务处理
    package com.mark.springcloud.controller;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.codingapi.tx.annotation.TxTransaction;
    import com.mark.springcloud.entities.Dept;
    import com.mark.springcloud.service.DeptClientService;
    
    @RestController
    public class DeptController_Consumer
    {
        @Autowired
        private DeptClientService service;
        //@TxTransaction(isStart = true)注解修饰该方法为事务发起方,开启事务组。
        @TxTransaction(isStart = true)
        @RequestMapping(value = "/consumer/dept/add")
        public Object add(Dept dept)
        {
            Object rtnObj = this.service.add(dept);
            int x = (int)(Math.random()*10);
            //事务发起方随机数小于5时,抛出异常,则事务参与方事务会回滚。否则正常执行,事务参与方事务正常提交。
            if (x < 5) {
                int m = 1/0;
            }
            return rtnObj;
        }
    }
    

    4.5.3 启动事务发起方

    启动spring boot 应用。

    4.6.测试事务

    调用事务发起方服务,事务正常受事务协调者控制,当发起方和参与方都正常执行无异常时,事务正常提交,否则回滚。

    4.7.总结

    Spring Cloud 集成LCN进行分布式事务控制使用简单,整个原理也很清晰。

    展开全文
  • 一、什么是分布式事务? 二、lcn的实现思路 Lcn 的本质就是提供了一个全局的事务管理器 本地事务的提交还是回滚,由全局的事务管理器决定 2.1 本地执行的状态怎么提交给全局事务? 使用切面拦截本地事务的执行...

    一、什么是分布式事务?

    在这里插入图片描述
    在这里插入图片描述

    二、lcn的实现思路

    Lcn 的本质就是提供了一个全局的事务管理器
    本地事务的提交还是回滚,由全局的事务管理器决定
    在这里插入图片描述
    在这里插入图片描述
    2.1 本地执行的状态怎么提交给全局事务?
    使用切面拦截本地事务的执行结果,使用tcp 发送给tx-manager
    2.2 本地事务的提交或回滚怎么实现?
    等待tx-manager通知自己

    三、lcn的使用

    3.1 下载lcn-manager (全局的事务管理器)
    在这里插入图片描述
    3.2 配置lcn-manager
    配置eureka 并且配置redis
    在这里插入图片描述
    在这里插入图片描述
    Redis:
    在这里插入图片描述
    3.3 启动lcn
    在这里插入图片描述
    在这里插入图片描述
    3.4 模拟转账服务
    数据库:
    在这里插入图片描述
    在这里插入图片描述
    3.4.1 add-service
    在这里插入图片描述
    3.4.2 decr-service
    在这里插入图片描述
    3.5 2 个微服务都需要添加依赖

    <dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-actuator</artifactId>
    		</dependency>
    		<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>
    		<!-- 依据druid 来实现的拦截功能 -->
    		<dependency>
    			<groupId>com.alibaba</groupId>
    			<artifactId>druid</artifactId>
    			<version>1.0.19</version>
    		</dependency>
    

    3.6 需要自定义数据库的连接池
    在2 个微服务的启动类或配置类里面都可以添加

    @Bean
    	public DataSource dataSource() {
    		DruidDataSource dataSource = new DruidDataSource();
    		dataSource.setUrl(env.getProperty("spring.datasource.url"));
    		dataSource.setUsername(env.getProperty("spring.datasource.username"));//用户名
    		dataSource.setPassword(env.getProperty("spring.datasource.password"));//密码
    		dataSource.setInitialSize(10);
    		dataSource.setMaxActive(50);
    		dataSource.setMinIdle(0);
    		dataSource.setMaxWait(60000);
    		dataSource.setValidationQuery("SELECT 1");
    		dataSource.setTestOnBorrow(false);
    		dataSource.setTestWhileIdle(true);
    		dataSource.setPoolPreparedStatements(false);
    		return dataSource;
    	}
    

    3.7 使用
    3.7.1 事务的发起者
    转账:在这里插入图片描述
    事务的参与者
    减少钱:
    在这里插入图片描述
    加钱:
    在这里插入图片描述
    3.7.2 添加配置文件
    在这里插入图片描述
    在这里插入图片描述
    3.8 启动2 个微服务
    在这里插入图片描述
    3.9 测试转账服务
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • TX-LCN 分布式事务框架(Spring Cloud 高级)一、 什么是分布式事务 分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。举个栗子: 电商系统中的...
  • 独立的服务,要想实现分布式事务,要把它注册到Eureka,与其他服务一样配置 2,到底下载哪个tx-manager,大部分文章给了下面这个地址,图2这个东西吗?我下载的并不是这个 3,gradle依赖问题:我用的...
  • 上图分布式事务的协调控制流程图2.LCN组件介绍三、 TC代理控制处理的流程图获取lcn原理PDF,项目文件代码,时序图,请关注公众号 猿之村,回复 分布式获取代码及资料写在最后,感谢点赞关注收藏转发 ...
  • tx-lcn分布式事务

    2019-07-26 14:11:43
    先回顾下spring本地事务@transactional 的使用 ... 深入理解什么是2PC、3PC及TCC协议 https://blog.csdn.net/bjweimengshu/article/details/8669...
  • LCN分布式事务框架原理详解4.0

    万次阅读 2018-10-11 17:16:07
    目录 一、首先介绍3.0与4.0之前的差异 1.、地址: 2、4.0添加升级如下功能: 二、LCN4.0原理 1、架构介绍 ...LCN是怎么去实现事务补偿呢? 5、插件机制 拟场景演示模 一、首先介绍3.0与4.0之前的差...
  • 什么是事务? 事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵...假设你已经清楚的知道了事务是什么
  • 最近笔者在面试过程中发现,分布式的... 那在这里就先要理解一下,什么是分布式事务管理,在单系统中,事务管理想必大家都很清楚,举个栗子,银行转账的过程中,张三的余额有100元,李四0元,张三要转50元给李四...
  • 分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位 于不同的分布式系统的不同节点之上。 举个栗子: 电商系统中的订单系统与库存系统 图中包含了库存和订单两个独立的微服务,每...
  • LCN分布式介绍

    2020-08-28 11:55:17
    什么是LCN;LCN与其他分布式事务的区别;LCN分布式事务的架构设计(原理介绍、劫持连接池计划);LCN源码地址
  • 分布式事务处理技术之LCN

    千次阅读 2019-09-18 14:13:14
    分布式事务LCN 第一章 分布式事务介绍一、什么是分布式事务二、XA 的两阶段提交方案三、TCC 解决方案四、分布式事务中间件解决方案第二章 LCN分布式事务处理框架介绍一、什么是LCN 框架二、LCN 框架原理及执行步骤三...
  • 一、 什么是分布式事务分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位 于不同的分布式系统的不同节点之上。 举个栗子: 电商系统中的订单系统与库存系统图中包含了库存和订单两个...
  • 分布式事务LCN使用-01

    2020-07-13 14:08:00
     LCN分布式事务系统通过代理数据资源,通TxManager(事务管理器)协调来完成对事务的统⼀控制,这样的操控⽅式使得框架对业务嵌⼊性⾮常低,在对本地代理资源的同时也通过排它锁防⽌其他⼈的 访问,从⽽也保障了事务...
  • 1. LCN框架原理及执行步骤(1) LCN框架执行原理是什么?在上图中,微服务 A,微服务 B,TxManager 事务协调器,都需要去 Eureka 中注册服务。Eureka 是用于 TxManager 与其他服务之间的相互服务发现。redis 是用于...
  • 一、 什么是分布式事务分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位 于不同的分布式系统的不同节点之上。 举个栗子: 电商系统中的订单系统与库存系统图中包含了库存和订单两个...
  • 1、什么是分布式事务 分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同...
  • 分布式事务LCN 第一章 分布式事务介绍一、什么是...
  • 分布式事务是什么?2.什么时候使用分布式事务3.分布式事务常见解决方案3.1 基于 XA 协议的两阶段提交3.2 消息事务+最终一致性 1.分布式事务是什么? 在分布式系统中,事务参与者在不同的分布式节点上或事务操作的...
  • 分布式事务学习-LCN

    2019-07-22 16:58:43
    分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位 于不同的分布式系统的不同节点之上。 图中包含了库存和订单两个独立的微服务,每个微服务维护了自己的数据库。在交易系 统的业务...
  • 什么是分布式事务,分布式事务的出现解决了什么? 分布式事务产生的原因: 从本地事务来看,我们可以看为两块,一个service产生多个节点,另一个resource产生多个节点。 service多个节点: 随着互联网快速发展,...
  • 分布式事务-Tx-lcn

    2020-04-05 00:22:41
    TX-LCN是什么?2. 框架定位3. 解决方案4. SpringCloud集成TX-LCN4.1 TX-LCN模块说明4.2 TM 配置与启动4.2.1 所需配置4.2.2 导入数据库脚本4.2.3 TM版本下载4.2.4 源码编译4.2.4 启动4.3 TC 微服务模块4.3.1 微服务...
  • Java极客技术的第 224篇原创文章最近做项目使用到了分布式事务,下面这篇文章将给大家介绍一下对分布式事务的一些见解,并讲解分布式事务处理框架 TX-LCN 的执...
  • 一致性(C)事务的执行必须保证系统的一致性,就拿转账为例,A有500元,B有300元,如果在一个事务里A成功转给B50元,那么不管并发多少,不管发生什么,只要事务执行成功了,那么最后A账户一定450元,B账户一定...
  • 当微服务启动达到一定时长以后,会报出这个错误信息,之后的微服务调用会进入fallback,需要手动重启事务的发起方,才可以正常使用,请问什么好的解决办法吗。   问题已经找到了,应该线程池的问题,现在有一个...
  • 什么情况下需要使用分布式事务? 使用的场景很多,先举一个常见的:在微服务系统中,如果一个业务需要使用到不同的微服务,并且不同的微服务对应不同的数据库。 打个比方:电商平台有一个客户下订单的业务逻辑,这...
  • 1. 什么情况下需要使用分布式事务? 使用的场景很多,先举一个常见的:在微服务系统中,如果一个业务需要使用到不同的微服务,并且不同的微服务对应不同的数据库。 打个比方:电商平台有一个客户下订单的业务逻辑...

空空如也

空空如也

1 2 3
收藏数 56
精华内容 22
关键字:

lcn分布式事务是什么