当前位置: 首页 > 编程日记 > 正文

分布式事务中间件 Fescar—RM 模块源码解读

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

前言

在SOA、微服务架构流行的年代,许多复杂业务上需要支持多资源占用场景,而在分布式系统中因为某个资源不足而导致其它资源占用回滚的系统设计一直是个难点。我所在的团队也遇到了这个问题,为解决这个问题上,团队采用的是阿里开源的分布式中间件Fescar的解决方案,并详细了解了Fescar内部的工作原理,解决在使用Fescar中间件过程中的一些疑虑的地方,也为后续团队在继续使用该中间件奠定理论基础。

目前分布式事务解决方案基本是围绕两阶段提交模式来设计的,按对业务是有侵入分为:对业务无侵入的基于XA协议的方案,但需要数据库支持XA协议并且性能较低;对业务有侵入的方案包括:TCC等。Fescar就是基于两阶段提交模式设计的,以高效且对业务零侵入的方式,解决微服务场景下面临的分布式事务问题。Fescar设计上将整体分成三个大模块,即TM、RM、TC,具体解释如下:

  1. TM(Transaction Manager):全局事务管理器,控制全局事务边界,负责全局事务开启、全局提交、全局回滚。
  2. RM(Resource Manager):资源管理器,控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。
  3. TC(Transaction Coordinator):事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。
    image.png

本文将深入到Fescar的RM模块源码去介绍Fescar是如何在完成分支提交和回滚的基础上又做到零侵入,进而极大方便业务方进行业务系统开发。

一、从配置开始解读

参考源码示例模块-dubbo-order-service.xml
上图是Fescar源码examples模块dubbo-order-service.xml内的配置,数据源采用druid的DruidDataSource,但实际jdbcTemplate执行时并不是用该数据源,而用的是Fescar对DruidDataSource的代理DataSourceProxy,所以,与RM相关的代码逻辑基本上都是从DataSourceProxy这个代理数据源开始的。

Fescar采用2PC来完成分支事务的提交与回滚,具体怎么做到的呢,下面就分别介绍Phase1、Phase2具体做了些什么。

二、Phase1—分支(本地)事务执行

Fescar将一个本地事务做为一个分布式事务分支,所以若干个分布在不同微服务中的本地事务共同组成了一个全局事务,结构如下。
image.png

那么,一个本地事务中SQL是如何执行呢?在Spring中,本质上都是从jdbcTemplate开始的,比如下面的SQL语句:

jdbcTemplate.update("update storage_tbl set count = count - ? where commodity_code = ?", new Object[] {count, commodityCode});

一般JdbcTemplate执行流程如下图所示:
image.png

由于在配置中,JdbcTemplate数据源被配置成了Fescar实现DataSourceProxy,进而控制了后续的数据库连接使用的是Fescar提供的ConnectionProxy,Statment使用的是Fescar实现的StatmentProxy,最终Fescar就顺理成章地实现了在本地事务执行前后增加所需要的逻辑,比如:完成分支事务的快照记录和分支事务执行状态的上报等等。

DataSourceProxy获取ConnectionProxy:
image.png

ConnectionProxy获取StatmentProxy:
image.png

在获取到StatmentProxy后,可以调用excute方法执行sql了
image.png

而真正excute实现逻辑如下:
image.png

  1. 首先会检查当前本地事务是否处于全局事务中,如果不处于,直接使用默认的Statment执行,避免因引入Fescar导致非全局事务中的SQL执行性能下降。
  2. 解析Sql,有缓存机制,因为有些sql解析会比较耗时,可能会导致在应用启动后刚开始的那段时间里处理全局事务中的sql执行效率降低。
  3. 对于INSERT、UPDATE、DELETE、SELECT..FOR UPDATE这四种类型的sql会专门实现的SQL执行器进行处理,其它SQL直接是默认的Statment执行。
  4. 返回执行结果,如有异常则直接抛给上层业务代码进行处理。

再来看一下关键的INSERT、UPDATE、DELETE、SELECT..FOR UPDATE这四种类型的sql如何执行的,先看一下具体类图结构:
image.png

为结省篇幅,选择UpdateExecutor实现源码看一下,先看入口BaseTransactionalExecutor.execute,该方法将ConnectionProxy与Xid(事务ID)进行绑定,这样后续判断当前本地事务是否处理全局事务中只需要看ConnectionProxy中Xid是否为空。
image.png

然后,执行AbstractDMLBaseExecutor中实现的doExecute方法
image.png

基本逻辑如下:

  1. 先判断是否为Auto-Commit模式
  2. 如果非Auto-Commit模式,则先查询Update前对应行记录的快照beforeImage,再执行Update语句,完成后再查询Update后对应行记录的快照afterImage,最后将beforeImage、afterImage生成UndoLog追加到Connection上下文ConnectionContext中。(注:获取beforeImage、afterImage方法在UpdateExecutor类下,一般是构造一条Select...For Update语句获取执行前后的行记录,同时会检查是否有全局锁冲突,具体可参考源码)
  3. 如果是Auto-Commit模式,先将提交模式设置成非自动Commit,再执行2中的逻辑,再执行connectionProxy.commit()方法,由于执行2过程和commit时都可能会出现全局锁冲突问题,增加了一个循环等待重试逻辑,最后将connection的模式设置成Auto-Commit模式

如果本地事务执行过程中发生异常,业务上层会接收到该异常,至于是给TM模块返回成功还是失败,由业务上层实现决定,如果返回失败,则TM裁决对全局事务进行回滚;如果本地事务执行过程未发生异常,不管是非Auto-Commit还是Auto-Commit模式,最后都会调用connectionProxy.commit()对本地事务进行提交,在这里会创建分支事务、上报分支事务的状态以及将UndoLog持久化到undo_log表中,具体代码如下图:
image.png
基本逻辑:

  1. 判断当前本地事务是否处于全局事务中(也就判断ConnectionContext中的xid是否为空)。
  2. 如果不处于全局事务中,则调用targetConnection对本地事务进行commit。
  3. 如果处于全局事务中,首先创建分支事务,再将ConnectionContext中的UndoLog写入到undo_log表中,然后调用targetConnection对本地事务进行commit,将UndoLog与业务SQL一起提交,最后上报分支事务的状态(成功 or 失败),并将ConnectionContext上下文重置。

综上所述,RM模块通过对JDBC数据源进行代理,干预业务SQL执行过程,加入了很多流程,比如业务SQL解析、业务SQL执行前后的数据快照查询并组织成UndoLog、全局锁检查、分支事务注册、UndoLog写入并随本地事务一起Commit、分支事务状态上报等。通过这种方式,Fescar真正做到了对业务代码无侵入,只需要通过简单的配置,业务方就可以轻松享受Fescar所带来的功能。Phase1整体流程引用Fescar官方图总结如下:

image.png

三、Phase2-分支事务提交或回滚

阶段2完成的是全局事物的最终提交或回滚,当全局事务中所有分支事务全部完成并且都执行成功,这时TM会发起全局事务提交,TC收到全全局事务提交消息后,会通知各分支事务进行提交;同理,当全局事务中所有分支事务全部完成并且某个分支事务失败了,TM会通知TC协调全局事务回滚,进而TC通知各分支事务进行回滚。

在业务应用启动过程中,由于引入了Fescar客户端,RmRpcClient会随应用一起启动,该RmRpcClient采用Netty实现,可以接收TC消息和向TC发送消息,因此RmRpcClient是与TC收发消息的关键模块。

public class RMClientAT {public static void init(String applicationId, String transactionServiceGroup) {RmRpcClient rmRpcClient = RmRpcClient.getInstance(applicationId, transactionServiceGroup);AsyncWorker asyncWorker = new AsyncWorker();asyncWorker.init();DataSourceManager.init(asyncWorker);rmRpcClient.setResourceManager(DataSourceManager.get());rmRpcClient.setClientMessageListener(new RmMessageListener(new RMHandlerAT()));rmRpcClient.init();}
}

上述代码展示是的RmRpcClient初始化过程,有三个关键类RMHandlerAT、AsyncWorker和DataSourceManager。RMHandlerAT具有了分支提交和回滚两个方法,分支提交或回滚的逻辑可以从这里开始看;AsyncWorker是一个异步Worker,主要是完成分支事务异步提交的功能,具有失败重试功能;DataSourceManager对数据源管理和维护。

下面分成两部分来讲:分支事务提交、分去事务回滚。

3.1、分支事务提交

在接收到TC发起的全局提交消息后,经RmRpcClient对通信协议的处理,再交由RMHandlerAT来完成对分支事务的提交,分支事务提交从RMHandlerAT.doBranchCommit()开始,但最后由AsyncWorker异步Worker完成,直接看AsyncWorker中的代码实现:
image.png

分支事务提交关键逻辑在doBranchCommits方法中:
image.png

该方法主要是批量删除UndoLog日志,但并未使用ConnectionProxy去执行删除SQL,可能原因是:1、完全没必要 2、考虑效率优先

同样,对于分支事务提交也引用Fescar官方一张图来结尾:
image.png

3.2、分支事务回滚

同样,分支事务回滚是从RMHandlerAT.doBranchRollback开始的,然后到了dataSourceManager.branchRollback,最后完成分支事务回滚逻辑的是UndoLogManager.undo方法。

 @Overrideprotected void RMHandlerAT:doBranchRollback(BranchRollbackRequest request, BranchRollbackResponse response) throws TransactionException {String xid = request.getXid();long branchId = request.getBranchId();String resourceId = request.getResourceId();String applicationData = request.getApplicationData();LOGGER.info("AT Branch rolling back: " + xid + " " + branchId + " " + resourceId);BranchStatus status = dataSourceManager.branchRollback(xid, branchId, resourceId, applicationData);response.setBranchStatus(status);LOGGER.info("AT Branch rollback result: " + status);}@Overridepublic BranchStatus DataSourceManager:branchRollback(String xid, long branchId, String resourceId, String applicationData) throws TransactionException {DataSourceProxy dataSourceProxy = get(resourceId);if (dataSourceProxy == null) {throw new ShouldNeverHappenException();}try {UndoLogManager.undo(dataSourceProxy, xid, branchId);} catch (TransactionException te) {if (te.getCode() == TransactionExceptionCode.BranchRollbackFailed_Unretriable) {return BranchStatus.PhaseTwo_RollbackFailed_Unretryable;} else {return BranchStatus.PhaseTwo_RollbackFailed_Retryable;}}return BranchStatus.PhaseTwo_Rollbacked;}

UndoLogManager.undo方法源码如下:
image.png
从上图可以看出,整个回滚到全局事务之前状态的代码逻辑集中在如下代码中:

AbstractUndoExecutor undoExecutor = UndoExecutorFactory.getUndoExecutor(dataSourceProxy.getDbType(), sqlUndoLog);
undoExecutor.executeOn(conn);

首先通过UndoExecutorFactory获取到对应的UndoExecutor,然后再执行UndoExecutor的executeOn方法完成回滚操作。目前三种类型的UndoExecutor结构如下:
image.png

undoExecutor.executeOn源码如下:
image.png

至此,整个分支事务回滚就结束了,分支事务回滚整体时序图如下:

image.png

引入Fescar官方对分支事务回滚原理介绍图作为结尾:

image.png

综合上述,Fescar在Phase2通过UndoLog自动完成分支事务提交与回滚,在这个过程中不需要业务方做任何处理,业务方无感知,因些在该阶段对业务代码也是无侵入的。

四、总结

本文主要介绍了RM模块的相关代码,将RM模块按2PC模式分成Phase1和Phase2分别进行介绍,从Fescar源码上看,整个源码结构清晰,有利于研发人员快速学习Fescar的原理。在使用方面,只需进行简单的配置,就可以享受Fescar带来的便捷功能,对业务做到了无侵入;同时在性能方面,Fescar在分支事务提交过程中采用异步模式,减少了全局锁的占用时间,进而提升了整体性能。后续,将继续学习Fescar的其它模块(TM、TC)与全局锁的实现逻辑,并做相关总结介绍。


原文链接
本文为云栖社区原创内容,未经允许不得转载。

转载于:https://my.oschina.net/u/3889140/blog/3011991

相关文章:

二维码检测哪家强?五大开源库测评比较

作者 | 周强来源 | 我爱计算机视觉(ID:aicvml)二维码已经进入人们的日常生活中,尤其是日本Denso Wave公司1994年发明的QR码,由于其易于检测、写入信息量大、提供强大的纠错机制,应用最为广泛,可…

linux 内核 出错-HP 方案

2019独角兽企业重金招聘Python工程师标准>>> SUPPORT COMMUNICATION - CUSTOMER ADVISORY Document ID: c03456595 Version: 1 Advisory: Red Hat Enterprise Linux 6 - "P4-Clockmod: Warning: EST-Capable CPU Detected" Messages Logged in /var/log…

Windows7 64bit VS2013 Caffe test MNIST操作步骤

在http://blog.csdn.net/fengbingchun/article/details/49849225中用Caffe对MNIST数据库进行训练,产生了model。下面介绍下如何将产生的model应用在实际的数字图像识别中。用到的测试图像与http://blog.csdn.net/fengbingchun/article/details/50573841中相同&#…

记住这35个英文单词,你就可以在RPA界混了!

无论是想玩转RPA(机器人流程自动化),还是有意了解、进入这项行业,只有先了解该领域的专有名词(行业术语),才能为之后的活动提供更多的可能。UiBot现为您编译整理了这份机器人流程自动化术语表&a…

福利 | 送你一张通往「2019 AI开发者大会」的门票

2019 AI开发者大会(AI ProCon 2019)是由中国IT社区CSDN主办的AI技术与产业年度盛会。多年经验淬炼,如今蓄势待发:2019年9月6-7日,大会将有近百位中美顶尖AI专家、知名企业代表以及千余名AI开发者齐聚北京,进行技术解读和产业论证。…

收缩日志文件夹

-- MSSQL2005 USE mastergo DECLARE dbname sysname;SET dbnameBSV100;-- 清空日志EXEC (DUMP TRANSACTION [dbname] WITH NO_LOG); -- 截断事务日志:EXEC (BACKUP LOG [dbname] WITH NO_LOG); -- 收缩数据库文件(如果不压缩,数据库的文件不会减小EXEC (DBCC SHR…

腾讯AI开源框架Angel 3.0重磅发布:超50万行代码,支持3种算法,打造全栈机器学习平台...

出品 | AI科技大本营(ID:rgznai100)【导语】2019年8月22日,腾讯首个AI开源项目Angel正式发布3.0版本。Angel 3.0尝试打造一个全栈的机器学习平台,功能特性涵盖了机器学习的各个阶段:特征工程,模…

路印协议受邀参加澳洲新南威尔士政府孵化器Haymarket HQ分享论坛

2019年2月15日,澳洲新南威尔士政府孵化器Haymarket HQ和Next Genius 社区联合举办了区块链解决方案分享论坛,路印协议CMO周杰受邀介绍当前交易所现状和路印协议的去中心化解决方案。参与此次论坛的还有区块链开发人员、企业家和去中心化技术爱好者&#…

一步一步指引你在Windows7上配置编译使用Caffe(https://github.com/fengbingchun/Caffe_Test)

之前写过几篇关于Caffe源码在Windows764位上配置编译及使用过程,只是没有把整个工程放到网上,最近把整个工程整理清理了下,把它放到了GitHub上。下面对这个工程的使用作几点说明:1. 整个工程Caffe在Windows7 64位VS2013下编译…

演示:思科IPS在线模式下Inline Interface Mode的响应行为(区别各个防御行为)

演示:思科IPS在线模式下Inline Interface Mode的响应行为演示目标:科IPS在线模式下InlineInterface Mode的响应行为。演示环境:仍然使用图5.16所示的网络环境。演示背景:在VLAN3的主机192.168.4.2上发起对主机192.168.4.1的漏洞扫…

【笔记】重学前端-winter

本文为:winter 发布在极客时间 【重学前端】系列课程的的笔记和总结支持正版哦: https://time.geekbang.org/col... 导语 如果深入进去了解,你会发现,表面上看他们可能是一时忘记了,或者之前没注意但实际上是他们对于前端的知识体…

如何用知识图谱挖掘商业数据背后的宝藏?

这是一个商业时代,一个数据为王的时代,也是一个 AI 迎来黄金发展期的时代。据史料记载,商业在商朝已初具规模。斗转星移,时光流转,到 2019 年,商业形式已发生翻天覆地的变化,但是商业的本质——…

通过define _CRTDBG_MAP_ALLOC宏来检测windows上的code是否有内存泄露

VS中自带了内存泄露检测工具&#xff0c;若要启用内存泄露检测&#xff0c;则在程序中包括以下语句&#xff1a; #define _CRTDBG_MAP_ALLOC #include <crtdbg.h> 它们的先后顺序不能改变。通过包括 crtdbg.h&#xff0c;将malloc和free函数映射到其”Debug”版本_malloc…

java.sql.SQLException: Data truncation: Truncated incorrect DOUBLE value

mysql 报这个异常&#xff1a;java.sql.SQLException: Data truncation: Truncated incorrect DOUBLE value update 表名 set col1 ? and col2 ? where id ? 改为&#xff1a; update 表名 set col1 ? , col2 ? where id ? 用逗号隔开

在Ubuntu14.04 64位上编译CMake源码操作步骤

在Ubuntu上通过apt-get install安装CMake并不是最新版的&#xff0c;这里记录下在Ubuntu上通过源码安装CMake的操作步骤&#xff1a;1. 卸载旧版CMake&#xff0c;执行以下命令&#xff1a;apt-get autoremove cmake如果卸载不掉&#xff0c;则通过执行以下命令删除&…

一份贪心算法区间调度问题解法攻略,拿走不谢

作者 | labuladong来源 | labuladong&#xff08;ID:labuladong&#xff09;【导读】什么是贪心算法呢&#xff1f;贪心算法可以认为是动态规划算法的一个特例&#xff0c;相比动态规划&#xff0c;使用贪心算法需要满足更多的条件&#xff08;贪心选择性质&#xff09;&#x…

css:z-index

针对position: absolute;解决position:relative;z-index固定定位层级显示问题转载于:https://blog.51cto.com/13507333/2352775

折半查找函数(from 《The C Programming Language》)

该函数用于判定已排序的数组array中是否存在某个特定的值value。这里假定数组元素以升序排列&#xff0c;如果数组array中包含value&#xff0c;则函数返回value在array中的位置&#xff08;介于0~n-1之间的一个整数&#xff09;&#xff1b;否则&#xff0c;该函数返回-1。 在…

C++中的explicit关键字介绍

C中的关键字explicit主要是用来修饰类的构造函数&#xff0c;被修饰的构造函数的类&#xff0c;不能发生相应的隐式类型转换&#xff0c;只能以显示的方式进行类型转换。类构造函数默认情况下声明为隐式的即implicit。隐式转换即是可以由单个实参来调用的构造函数定义了一个从形…

Redis的集群模式

集群 即使使用哨兵&#xff0c;此时的Redis集群的每个数据库依然存有集群中的所有数据&#xff0c;从而导致集群的总数据存储量受限于可用存储内存最小的数据库节点&#xff0c;形成木桶效应。由于Redis中的所有数据都是基于内存存储&#xff0c;这一问题就尤为突出了尤其是当使…

刚上线就报名2000人!8位大牛免费讲座,再不报名就满额了!

今年是CSDN的第20年&#xff0c;我们已经不再满足解决你的技术问题&#xff0c;还要帮你解决人生大事&#xff01;为了让你飞黄腾达&#xff0c;我们特别邀请到了8位大牛老师进行直播&#xff0c;他们已经实现了成为技术总监、创业、财富自由的梦想&#xff0c;这场直播&#x…

排序算法之插入排序

插入排序一般分为直接插入排序和二分插入排序。一、直接插入排序&#xff1a;直接插入排序又可以分为前插和后插&#xff0c;不过虽然是这样分&#xff0c;只是寻找地点的方向不一样而已。“前插”就是从头开始找合适的位置&#xff0c;“后插”就是从后面开始找合适的位置。直…

C++中#error/assert/static_assert的区别及使用

C 语言支持可帮助您调试应用程序的三个错误处理机制&#xff1a;#error 指令、static_assert 关键字和 assert (CRT) 宏。所有的三种机制都会发出错误消息。#error可看做预编译期断言&#xff0c;甚至都算不上断言&#xff0c;仅仅能在预编译时显示一个错误信息&#xff0c;它能…

读完ACL 2019录取的30篇知识图谱论文,我发现了这5点趋势

作者 | Michael Galkin译者 | Freesia编辑 | 夕颜出品 | AI科技大本营&#xff08;ID: rgznai100&#xff09;【导读】近年来&#xff0c;自然语言处理领域中广泛应用的知识图谱&#xff08;KGs&#xff09;正在不断地吸引人们的目光&#xff0c;此次 ACL 2019 中的有关于知识图…

力扣(LeetCode)933

题目地址&#xff1a;https://leetcode-cn.com/probl...题目描述&#xff1a;写一个 RecentCounter 类来计算最近的请求。 它只有一个方法&#xff1a;ping(int t)&#xff0c;其中 t 代表以毫秒为单位的某个时间。 返回从 3000 毫秒前到现在的 ping 数。 任何处于 [t - 3000, …

2013年10月1日C#随机数

最近开始接触C跟C#&#xff0c;总是有人说女生本来就不适合做程序&#xff0c;就连今天都听到有人这样跟我讲&#xff0c;不过呢没有关系&#xff0c;我相信男生不一定比女生厉害多少&#xff0c;就好像我身边就有一位男生就总是觉得我的程序比他好一点就是理所当然的&#xff…

C/C++中inline/static inline/extern inline的区别及使用

引入内联函数的目的是为了解决程序中函数调用的效率问题&#xff0c;也是用内联函数取代带参宏定义&#xff08;函数传参比宏更加方便易用&#xff09;inline关键字用来定义一个类的内联函数。在类体中和类体外定义成员函数是有区别的&#xff1a;在类体中定义的成员函数为内联…

RISC-V架构上的Debian和Fedora现状

RISC-V仍然是开源/Linux用户非常感兴趣的&#xff0c;因为它是免版税且完全开放的CPU架构。部分原因是由于缺乏经济实惠的RISC-V硬件&#xff0c;限制了开发人员在这种架构上的更多工作&#xff0c;Linux发行版支持的RISC-V状态各不相同&#xff0c;但近年来至少有所改善。在上…

字节跳动李航:自学机器学习,研究AI三十载,他说AI发展或进入平缓期

作者 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;【导读】一阵凉风吹过人工智能&#xff0c;让这个曾是燥热的领域逐渐冷却下来&#xff0c;留下的是扎实地在做研究的人、机构、企业。先后在 NEC 公司中央研究所、微软亚洲研究院、华为诺亚方舟实验室从事和…

PC上安装MAC X Lion

PC上安装MACXLion网上关于如何在PC下安装MAC的文章已近不少了&#xff0c;但对于一些初学者在实践当中会遇到各种问题&#xff0c;以下视频资料为大家展示两种虚拟机安装MacOS。1.VmwareWorkstation在虚拟机中安装首先将插件装好&#xff08;在远景上下载&#xff09;&#xff…