编写自己的分布式框架—可靠消息事务最终一致性(三)

回顾

上一篇文章我们说到,各种分布式事务解决方案的特点,其中最后提到了可靠消息事务最终一致性这种解决方案,而我们这篇文章的标题也是它,没错,我们接下来要详细的分析该解决方案的实现细节了,上一篇文章在介绍该解决方案时,已经说了那个执行流程分析图,仅仅只是一个粗略图而已,实际上,可靠消息事务最终一致性的设计是非常复杂的。那么为什么要花那么多时间来详细分析它的实现细节呢?原因是接下来我们开发的这个分布式事务框架,就是使用可靠消息事务最终一致性的方案,选择它的理由很简单,就是因为它比较复杂,所以实现的过程中,能学到的技术和得到的锻炼比较多。

总体设计

先来看看以下这张设计图:

image.png

这张设计图是在上一章的执行流程基础上,再补充了一些细节,实际上,要完成可靠消息最终一致性,并不是仅仅依靠消息队列就行了,还需要很多其他组件共同协作,这些组件在以不修改业务方法的前提下,通过组件或者扩展的方式整合到项目中,具有可插拔性,这样的话,才能做到对业务项目的侵入性低的目的。
  那么通过上述的总体设计流程图,我们可以提取出以下几个组件:1、拦截器组件(Interceptor),2、事务协调组件(Coordinator),3、事务日志存储组件(Repository),4、可靠消息组件(MQ),5、补偿调用组件(Invoker),6、定时器组件(Scheduler),7、初始化组件(Initiator)。接下来,我们详细的看看,每一个组件是什么,做什么,并且它们之间如何协调工作的。
注意:以下所涉及的代码不是真正的实现,而是伪代码

1、拦截器组件(Interceptor)

通过设计图的分析,我们可以知道,有两个地方需要拦截的,一个是在发起RPC请求时,需要对请求进行拦截,一个是RPC请求到达了远程目的方法后,执行方法前的拦截。

● 发起RPC请求的拦截
  在发起RPC请求的拦截器中,我们需要告诉事务协调者,自己的角色是事务发起者的角色,这一步是至关重要的,因为一个事务发起者需要保存这多个事务参与者的信息,举个例子:转账(transfer)业务方法开启了分布式事务,并且transfer方法中有两个远程方法,分别是扣钱(decrease)和加钱(augment)。那transfer就是事务发起者,decrease和augment就是事务参与者。
  transfer在分别调用decrease和augment者两个远程方法时,先进入了拦截器,把decrease和augment这两个事务参与者的信息添加到transfer这个事务发起者中,也就是我们常说的一对多关系。
  在执行完了transfer方法后,整个transfer这个事务发起者下有多少个事务参与者都添加完毕了,接下来就把自己的这个事务发起者角色告诉事务协调者,到这里,发起RPC请求的拦截器做的事情就结束,接下来就是事务协调者做的事情了,我们后面在来讲事务协调者。 结合以下分析图理解:

image.png

● 执行业务方法前的拦截
  跟发起RPC请求的拦截类似,在RPC请求到达了目的方法后,开始执行方法,但在执行方法前,我们还需要做一些事情,就是把自己作为事务参与者的角色告知事务协调者,还是以拦截器的形式做这些事情,但是拦截器又如何知道自己将要执行的业务方法就是事务参与者呢?这就要回到RPC请求上了:transfer远程RPC调用decrease和augment时,需要给这两个地址追加一个参数,该参数具体是什么东西没有关系,只要能知道事务发起者是谁就行了(暂且我们叫该参数为transactionId),那么decrease和augment所在的过滤器获取RPC参数名为transactionId的值,发现有值,那就把自己确定问事务参与者,并且通过transactionId的值知道,自己所属的事务发起者是谁。 结合以下分析图理解:


image.png

2、事务协调组件(Coordinator)

事务协调者是所有分布式事务解决方案都会有的一个核心处理器,它作为各个分布在不同JVM中的本地事务间接通讯的桥梁。在可靠消息事务最终一致性的解决方案中,它主要负责事务的发起、事务的参与、事务的提交、事务出错处理、发送事务MQ消息等,凡是涉及到事务的生命周期操作,都经过它做统筹处理。它直接操作下一个要讲解的组件—事务日志存储组件(Repository)。

3、事务日志存储组件(Repository)

事务日志存储组件主要负责储存事务日志的操作,事务日志的核心信息为:事务id、事务类和方法,事务状态、事务角色、事务参与者集合。所以针对事务的操作非常多,会涉及到多线程和大并发的问题,所以这里到时候需要考虑线程安全和支持大并发的设计。 结合以下分析图理解:


image.png

4、可靠消息组件(MQ)

可靠消息组件顾名思义,就是发送MQ消息的,而消息体就是:事务发起者中的各个事务参与者详细信息,还是以我们上面的例子说明:transfer在分别调用decrease和augment者两个远程方法,transfer是事务发起者,decrease和augment是transfer的事务参与者,在transfer方法执行完后,得到一个事务信息,该信息传给了事务协调者,事务协调者会遍历该事务中的参与者列表,每遍历到一个事务参与者,就往该参与者所监听的MQ消息地点发送消息,消息体的核心信息为:“参与者的业务方法”。这样的话参与者如果在执行业务的过程中报错了,还可以到MQ中获取消息,重新执行业务方法。结合以下分析图理解(为了分析图更直观,删减了一些流程):


image.png

5、补偿调用组件(Invoker)

消息补偿组件起到的作用就是:各个事务参与者在各种异常情况下,没法正常执行业务方法,比如:事务发起者transfer调用了decrease和augment,但是augment所在的服务器宕机,导致不能接受RPC请求,这样事务就有问题了,decrease扣钱成功,augment加钱失败。这时MQ就起作用了,我们刚刚已经知道,在transfer方法执行完后,会给MQ的decrease和augment地点发送消息,消息体有个核心信息:“执行的业务方法”。那这样,augment所在的服务器重启后,马上监听到MQ的augment地点消息,然后把消息交给事务补偿调用组件(Invoker),Invoker重新调用消息体中的业务方法,完成事务补偿。 同理,如果augment所在的服务器没有宕机,但却在执行业务方法的过程中报错,则MQ会新增一条消息,等着augment获取,并重新执行业务方法。那为什么报错了会自动往MQ发消息呢?这个涉及到定时器组件(Scheduler),我们下面再详细分析定时器的作用。
  但这里需要特别注意的是,Invoker需要做好幂等性的操作,因为transfer方法执行完了,事务协调者就会往decrease和augment两个事务参与者监听的消息地点发送消息,所以,不管decrease和augment是否正常执行,消息都会发送出去,那问题就来了,通过RPC已经正常调用了一次,然后监听到MQ消息又调用了一次,就调用了两次了,所以事务补偿调用组件需要做好幂等性,防止业务方法执行多次。

6、定时器组件(Scheduler)

定时器组件(Scheduler)就是我们刚刚提到的一种情况的问题:服务器正常运行,能接收RPC远程调用请求,但是执行过程中报错,那这时,定时器就起作用了,定时器在项目初始化时就需要设置好。
  业务方法执行报错,进入捕捉异常流程,通知事务协调者,把对应的事务日志状态修改为“错误”状态,然后定时器每隔一段时间扫描状态为“错误”的事务日志,扫描到之后,即往MQ发送消息,那接下来的流程,又回到了事务补偿的流程了。事务补偿成功后,需要把对应的事务日志状态修改为“提交”状态。结合以下分析图理解(为了分析图更直观,删减了一些流程):


image.png

7、初始化组件(Initiator)

这一个组件就不用多说了,我们设置定时器,监听MQ的Destination,还有做一些初始化参数设置等,都是需要在这里执行。

总结

分布式事务本身就是一件非常复杂的事情,所以在设计阶段就要考虑的比较完善,这样在具体实现的时候才不会有太多问题。而通过这篇文章的讲解,已经从宏观上了解了这种“可靠消息事务最终一致性”解决方案的来龙去脉,相当于设计稿就有了。那接下来,我们就要根据这个设计稿来做具体功能的实现。在写代码实现的过程中,由于会接触到更细节的问题,这些细节又不可能在设计阶段面面俱到,所以很可能会出现具体实现与设计不一样的情况,这是正常的,只要在合理的调整范围内,大体流程上并没有改变就行了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348

推荐阅读更多精彩内容