EasyTransaction是一个全功能的分布式事务框架,以下特性摘抄自其首页:https://github.com/QNJR-GROUP/EasyTransaction

  • 一个框架包含多种事务形态,一个框架搞定所有类型的事务
  • 多种事务形态可混合使用
  • 高性能,大多数业务系统瓶颈在业务数据库,若不启用框架的幂等功能,对业务数据库的额外消耗仅为写入25字节的一行
  • 可选的框架自带幂等实现及调用错乱次序处理,大幅减轻业务开发工作量,但启用的同时会在业务数据库增加一条幂等控制行
  • 业务代码可实现完全无入侵
  • 支持嵌套事务
  • 无需额外部署协调者,不同APP的服务协调自身发起的事务,也避免了单点故障
  • 分布式事务ID可关联业务ID,业务类型,APPID,便于监控各个业务的分布式事务执行情况

本文主要分享EasyTransaction core中各个package的作用其主要实现。

请先阅读 Seata架构的比对思考 https://www.cnblogs.com/skyesx/p/10674700.html ,再结合 代码以及demo调试过程看这篇,直接看的话这里的点太零碎了

一、context包

主要类

LogProcessContext

其用于存储ET事务的上下文信息。在开启ET事务(第一次ET远程调用,或者主动调用startSoftTrans方法)时,将创建本类的实例并将其与Spring的本地事务上下文绑定,通过:

TransactionSynchronizationManager.bindResource()

执行绑定。当需要取得ET上下文时,通过

TransactionSynchronizationManager.getResource()

取得。

ET上下文中包含的主要内容有:

  • 最终事务状态
  • 全部的全局事务日志
  • 未Flush到外部的全局事务日志
  • 事务ID等内容

二、包core

本包主要类为

EasyTransFacade
TransactionHook
ConsistentGuardian
ExecuteCacheManager

类EasyTransFacade

其定义了业务调用方的接口,只包含两个:

public void startEasyTrans(String busCode,long trxId);

public <P extends EasyTransRequest<R,E>,E extends EasyTransExecutor, R extends Serializable> Future<R> execute(P params);

第一个用于开启全局事务,主要的操作为:

  • 挂载TransactionHook到当前的Spring本地事务中,使得可以在关键节点(如本地事务提交前、本地事务回滚后等等)嵌入ET的代码
  • 将 LogProcessContext 绑定到当前的Spring本地事务,使得ET可以在当前Spring本地事务中随时取得ET全局事务的状态。
  • 在当前已开启的本地事务中,写入一条事务执行记录到业务库中,其对Crash恢复时识别全局事务的状态起关键作用

第二个表示执行某个远程事务方法。

  • 通过调用参数Object对应的Class获取对应的处理器(如TCC处理器,可靠消息处理器等)并执行调用,具体调用的形态后续专门的章节再继续

基于注解的接口调用也是通过这两个方法封装而成。

类TransactionHook

其为ET框架代码与Spring原生事务的主要交界点,ET通过TransactionSynchronization定义的方法,在Spring本地事务执行过程中,扩展支持了全局事务。主要扩展了以下两个方法

beforeCommit(boolean readOnly)
afterCompletion(int status)

beforeCommit方法将会

  • 在Spring本地事务提交前将所有未落盘的全局事务日志落盘
  • 并执行所有未执行的远程调用(ET会尽量延后全局事务以此堆积并批量执行)
  • 若有不成功的全局事务,则抛出异常,回滚事务(包括本地以及全局)

afterCompletion方法将会

  • 获取本地事务的最终结果(提交/回滚/未知)以及 ET父级事务的状态(提交/回滚/未知)来决定本级ET事务的最终状态(提交/回滚/未知)
  • 获得最终的本级ET事务状态后,异步执行最终一致处理(调用consistentGuardian.process)

类ConsistentGuardian

本类用于处理ET全局事务的最终一致,例如TCC的Conifrim/Cancel,可靠消息的发送消息。

最终一致处理通常会在同步操作(TCC的TRY等)对应的本地事务执行完成后抛到线程池异步执行,但执行失败的话,会有兜底的补偿(recovery包),后续再详细讲述

该类的主要工作机制是根据之前写入的全局事务日志,获取日志对应的处理器(如从TCC的事务日志获取对应的TCC日志处理器),以此

  • 判断当前ET事务的最终状态(若当前ET事务状态仍未确定的话)
  • 传入最终ET事务状态到日志处理器,依次处理对应的事务日志,处理的典型过程例子:
    • 若存在TRY方法对应的日志
    • 并且找不到TRY对应的CONFRIM/CANCEL日志
    • 则根据ET最终事务状态,调用对应CONFIRM/CANCEL方法

类ExecuteCacheManager

本类主要服务于ET的以下期望

  • 批量写入ET事务日志(以减少IO)
  • 批量并发执行远程业务调用(以减少串行等待远程相应时间)

其主要实现的是,

  • 对每个传入的Calleble对象都返回一个经过改写的Futrure对象
  • 当任意一个Futrue的get方法都没有被调用前,所有之前传入的Callable对象都不会执行。
  • 当任意一个Future的get被调用时,所有callable都会被批量执行,这里包含了批量写入日志以及批量并发执行远程调用

三、包datasource

主要包含以下两个接口,其主要作用于业务数据源。

DataSourceSelector
TransStatusLogger

类DataSourceSelector

该类主要用于获取当前事务/请求对应的数据源及其事务管理器,若应用有多个业务数据源,则需要自行实现对应的数据源选择器,主要包含以下方法

DataSource selectDataSource(String appId,String busCode,long trxId);
DataSource selectDataSource(String appId,String busCode,EasyTransRequest<?, ?> request);

第一个方法是开启ET事务时候选择对应的数据源

第二个方法是被调用方接受到请求时选择对应的数据源(用于幂等、防悬挂处理,若不需要可忽略)

该接口包含一个默认实现,当只有单数据源时,可以直接用该实现

SingleDataSourceSelector

类TransStatusLogger

该类主要用来读写用于判断ET事务状态的记录,该记录会在ET事务开启时,写入当前的数据库表中,随着业务对应事务(Spring本地事务)提交而提交,回滚而回滚。

更具体请直接看实现

四、包executor

该包存储的是事务发起方(远程服务调用方)相关处理类的位置,不同的事务类型(TCC,可靠事务等)有不同的Executor,以TCC为例讲解,其他的事务类型实现都类似。

TccMethodExecutor

该类实现了三个接口

  • EasyTransExecutor
  • LogProcessor
  • DemiLogEventHandler

EasyTransExecutor接口定义了方法

	<P extends EasyTransRequest<R,E>,E extends EasyTransExecutor,R  extends Serializable> Future<R> execute(Integer sameBusinessCallSeq, P params);

该方法供类EasyTransFacade.execute使用,其对应的是执行TCC里的TRY方法,具体的,它

  • 将TRY方法调用对应的RPC请求包装成Runnable类
  • 构建本次调用对应的全局事务日志(主要包含本次调用的具体参数、对应远程方法等)
  • 然后传入上面章节提到的类ExecuteCacheManager方法中

LogProcessor接口定义了如何处理事务日志,其包含一个主要方法

	boolean logProcess(LogProcessContext ctx, Content currentContent)

该方法将会判断,如果传入的日志类型是PreTccCallContent(TCC TRY请求对应的日志)的话,将会监听该日志最终的配对信息(类ConsistentGuardian会在处理当前ET事务的日志后,发送消息,告知所有需要配对的日志的配对结果),如果

  • 监听到成功配对(找到CONFIRM或者CANCEL对应的日志)的消息,则不再做后续处理
  • 监听到配对失败(没有存在对应的CONFIRM/CANCEL日志)的消息,则根据当前的ET事务状态执行对应的CONFIRM或者CANCEL操作,并记录对应的日志

其他的事务形态的实现也类似,不再赘述

五、包recovery

用于兜底恢复事务,实现最终一致。

代码不复杂,可以自行查看。

六、包Filter

该包主要用于实现ET对应的Filter,该Filter作用于被调用端。我们可以通过实现ET的Filter扩展被调用端的功能,如处理幂等、处理嵌套事务、增加调用上下文的处理等等。

七、包idempotent

实现幂等、方悬挂等处理对应的包

幂等及防悬挂处理的主要原理:

  • 当远程调用过来时,写入调用日志到当前的开启的业务日志中,并记录 调用对应的ID,调用参数对应的MD5
  • 有结果返回时就将结果更新存储到日志中
  • 当有重复请求过来时,就检查ID对应的记录是否存在,若存在则检查参数的MD5是否一致,若一致则返回之前的存储结果
  • 防悬挂也类似,在上述的日志中,将会记录调用的方法是什么,如
    • 当找不到请求对应日志时,但当前为cancel操作的话,框架将直接返回成功
    • 上述cancel已经成功执行后,try方法再来到时,发现cancel已经执行,就直接将try报错返回

八、包idgen

用于生成ET的分布式事务ID,当自行制定ID时,本包对应的方法不会被调用。当不指定ID时,将会自动生成一个。

九、包log

定义ET事务日志对应的Class,以及其读写接口。

事务日志在之前TccExecutor等章节已提到,不再赘述。

需要扩展事务日志存储实现的,直接实现以下接口即可

TransactionLogReader
TransactionLogWritter

(ET事务日志的读写不需要与业务事务在同一个事务中,也不能在同一个事务中)

十、包master

用于在同一个appId中选择一个作为master进行兜底最终一致补偿的包。

实际上选择不需要太精确,任意一个appId下的实例均可,也可以同时有多个master存在(但目前没有意义,也会浪费性能)

十一、包monitor

用于提供ET实例状态的包,可供Dashboard,监控等扩展使用

十二、包protocol

供客户直接定义分布式事务服务的包,其包含一些客户直接使用的 父类、配置接口、配置注解等

十三、包provider.factory

从Spring中获取并存储对应的bean实现,以便于快速方便地通过ET对应的定义,取得对应bean

十四、包queue

若要扩展新增对应的消息队列实现,则实现这个包对应的接口

十五、包rpc

同上

十六、包serialization

ET框架所使用的序列化形式,可自行扩展

十七、包stingcodec

用于压缩字符串,将字符串替换为数字id,以提高存储效率

EasyTransaction主要源码分析的更多相关文章

  1. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  2. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

  3. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  4. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  5. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  6. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

  7. java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

  8. ABP源码分析二:ABP中配置的注册和初始化

    一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...

  9. ABP源码分析三:ABP Module

    Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...

随机推荐

  1. 机器审核图片学习(1)pornDetector

    a) https://github.com/bakwc/PornDetector 封装了两个库,opencv与scikit-learn 另外一种法师封装了opencv与tensorflow

  2. Redis 宝典 | 基础、高级特性与性能调优

    转载:Redis 宝典 | 基础.高级特性与性能调优 本文由 DevOpsDays 本文由简书作者kelgon供稿,高效运维社区致力于陪伴您的职业生涯,与您一起愉快的成长.     作者:kelgon ...

  3. Method and apparatus for establishing IEEE 1588 clock synchronization across a network element comprising first and second cooperating smart interface converters wrapping the network element

    Apparatus for making legacy network elements transparent to IEEE 1588 Precision Time Protocol operat ...

  4. 【003】【Java虚拟机——对象死亡的判断】

    对象死亡! 垃圾收集器在对堆进行回收前,首先要做的事情就是要确定这些对象之中哪些还"存活"着, 哪些已经"死去" (即不可能再被不论什么途径使用的对象). 1)  引用计 ...

  5. matlab 类型转换(类型判断)

    char:Convert to character array,转换为字符数组:matlab 下没有 str 字符串类型转换: char(0-255) ⇒ ASCII 码的转换: im2double( ...

  6. 如何把Go调用C的性能提升10倍?

    目前,当Go需要和C/C++代码集成的时候,大家最先想到的肯定是CGO.毕竟是官方的解决方案,而且简单. 但是CGO是非常慢的.因为CGO其实一个桥接器,通过自动生成代码,CGO在保留了C/C++运行 ...

  7. 关于fastjson用法

    fastjson 是一个性能很好的 Java 语言实现的 JSON 解析器和生成器,来自阿里巴巴的工程师开发. public static final String toJSONString(Obje ...

  8. log4net使用记录

    1.在程序中引用log4net.dll 2.添加-新建配置文件Log4Net.config,并在文件属性中“复制到输出目录”选中“始终复制”,文件内容如下: <?xml version=&quo ...

  9. Prism框架在项目中使用

    本文大纲 1.Prism框架下载和说明 2.Prism项目预览及简单介绍. 3.Prism框架如何在项目中使用. Prism框架下载和说明 Prism框架是针对WPF和Silverlight的MVVM ...

  10. 解决WPF的ScrollViewer在使用触摸屏时,滑到尽头窗口抖动的情况

    原文:解决WPF的ScrollViewer在使用触摸屏时,滑到尽头窗口抖动的情况 wpf的ScrollViewer在触摸条件下 默认在尽头时会有一个窗口一起被拖动的FeedBack,但对用户的交互很不 ...