谈谈分布式事务之二:基于DTC的分布式事务管理模型[下篇]

时间:2022-04-22
本文章向大家介绍谈谈分布式事务之二:基于DTC的分布式事务管理模型[下篇],主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

[续上篇]当基于LTM或者KTM的事务提升到基于DTC的分布式事务后,DTC成为了本机所有事务型资源管理器的管理者;此外,当一个事务型操作超出了本机的范围,出现了跨机器的调用后,本机的DTC需要于被调用者所在机器的DTC进行协助。上级对下级(包括本机DTC对本机所有资源管理器,以及上下级DTC)的管理得前提是下级在上级那里登记,即事务登记(Transaction Enlist)。所有事务参与者,包括所有资源管理器和事务管理器(即DTC)在进行了事务等级完成之后形成了一个树形的层级结构,该结构的形成是后续的事务提供成为可能,因此我们将其称之为事务提交树(Transaction Commit Tree)。

一、事务登记(Transaction Enlisting)和事务提交树(Transaction Commit Tree)

事务登记的目的在于建立起事务参与者(只要指资源管理器和事务管理器DTC)之间的关系,促进相互之间的协作。我们先来看看整个事务登记是如何进行的,整个事务登记流程大致如图1所示。

图1 事务登记的流程

图1所示的事务涉及到部署与两台机器(Machine A和Machine B)上两个服务之间的交互,事务由Service初始化,在调用Service2的时候被传播到Machine B,从而将分布于两台机器的资源管理器纳入到同一个事务之中。接下来,我们对整个流程进行详细介绍。

首先,Service A开始一个新的事务,并将其最为当前执行上下文的环境事务(Ambient Transaction)。当Service A调用本机的资源管理器的时候,会将该资源管理器纳入到本事务之中(对于我们后续要介绍的System.Transactions事务,像SQL Server、Oracle以及MSMQ等资源管理器都能够自动感知到当前的环境事务)。此时,资源管理器(RM)向本机的DTC进行事务登记,从此DTC和资源管理器之间建立起了上下级关系。

当Service A在调用Service B的时候,会将当前事务的一些信息,比如分布式事务的ID以及关于本机DTC相关信息,封装在消息中(一般是SOAP报头)向对方传递。当Service B接受到服务调用请求消息,会将事务相关的信息提取出来在本地重建事务,并将其作为当前的环境事务,该事务和原事务具有相同的ID。同时,根据得到的关于Machine A的DTC相关信息,让本机的DTC对Machine A的DTC进行事务登记,进而使两台机器的DTC确立了上下级关系。

和Service A访问本机的资源管理器一样,Machine B的资源管理器被Service B调用并被纳入当前事务的时候,会向本机DTC进行事务登记。

当上面所述的事务登记流程结束后,参与整个分布式事务的DTC和资源管理器形成了如图2所示的树形层次结构,由于该结构的构建主要是为了后面对整个事务的提交服务的,所以我们一般将其称为事务提交树(Transaction Commit Tree)。事务提交树的根为事务初始化服务所在机器的DTC,在整个事务提交过程中,它是总的协调者,又被称为全局提交协调器(GCC:Global Commit Coordinator )。资源管理器充当事务提交树的叶子节点,它们的父节点为本机的DTC。分布于不同机器的DTC按照事务传播的路径形成上下级关系。

图2 事务提交树

事务提交树的构建使得对分布式事务的提交成为可能,分布式事务的提交采用两阶段协议实现,接下来我们详细介绍基于两阶段提交协议的事务提交机制。

二、基于两阶段提交(2PC:Two-Phase Commit)协议的事务提交机制

不同于基于单一资源管理器的本地事务,在一个分布式环境中时实现一个涉及到多个资源管理器的分布式事务,实现事务的ACID四大属性,要麻烦得多。当事务初始化服务(应用或者组件,为了更佳贴近WCF,我们都称服务)完成所有相关的操作,决定提交该事务。对于分布式事务的提交,最终的结果有两个:如果所有的操作能够顺利完成,需要持久化的数据被相应的资源管理器写入到目标资源;如果任何一个环节失败,所有持久化资源管理器将数据恢复到原来的状态。分布式事务的整个提交过程,采用两阶段提交(2PC:Two-Phase)Commit协议完成。顾名思义,“两阶段提交”意味整个整个事务提交阶段分两个阶段,我们现在就来详细介绍分别在这两个阶段中,都在做些什么。

注:在事务初始化服务决定提交事务之前,任何一个参与者均可以中止(Abort)该事务,任何一个参与者对事务的中止都将导致整个分布式事务的回滚。

1、第一阶段(Phase I):准备(Prepare)阶段

在第一个阶段,作为根节点的DTC沿着事务提交树的路径,向所有事务的参与者发起请求,要求它们对本地事务的结果进行投票。被请求的参与者将本地事务结果返回给自己的上级,对于资源管理器,自己的上级就是本机的DTC;如果自己本身就是DTC,那么自己得上级就是位于事务提交树父节点的DTC。根据具体事务操作执行的情况,参与者投票的类型包括如下三种:

  • 就绪(Prepared):参与者同意对事务的提交,并承诺在接收到真正的提交请求后完成本地的提交任务;
  • 只读(Read-only):参与者同意对事务的提交,但是不希望接收真正的事务提交请求;
  • 中止(Aborted):参与者要求中止事务。

对于一个非根节点DTC来说,当它从父节点接收到标准的“准备”请求后,会立即将该请求沿着事务提交树发送给自己的下级(本机的资源管理器和下级DTC),然后根据从下级接收的所有投票结果,计算出自己投票的结果。具体的算法是:如果所有的投票结果都是“就绪”和“只读”,对应的结果是“提交”,反之,如果任何一个投票结果是“中止”,则最终的结果就是“中止”,换句话说,任何一个事务参与者具有一票否决权最后。最后,DTC将计算出来的投票结果反馈给自己的上级。

当根节点DTC接收到隶属于自己的所有资源管理器和下级DTC的投票结果后,采用于上面一样的算法决定整个分布式事务的最终结果。当根节点DTC决定了事务最终的结果后,整个提交过程进入第二阶段。

注:我们可以设置事务的超时时限,如果根节点DTC在该时限内没有介绍到所有参与者的投票请求,会对整个分布式事务做出回滚的决定。

2、第二阶段(Phase II):提交(Commit)或中止(Abort)

作为事务提交树根节点的DTC根据最终的事务结果,对整个事务进行最终的提交或者中止操作。同样是沿着事务提交树的路径,提交或者中止请求被广播出去。相应的资源管理器根据从本机DTC获得的请求,实施最终的提交或者恢复操作。当事务参与者完成了各自就的任务后,类似于第一个阶段的投票,会讲执行的结果沿着事务提交树逆向回馈给作为根节点的DTC。

根节点DTC只有接收到所有事务参与者的基于各自事务处理的回复,才能确保整个事务被成功提交或者回滚。那么如果事务的参与者完成了第一阶段的投票,网络断开,那将如何呢?这就涉及到对未决(In-Doubt)事务的处理。

3、未决(In-Doubt)事务的处理

对于某个分布式事务的参与者(DTC或者资源管理器)来说,在第一阶段向上级(事务提交树的父节点)投票表明提交就绪(Prepared)之后,直到它接收到根节点DTC最终的提交或者回滚的请求,它并不知晓本地事务的结果。在这期间,如果出现当机并重启,本地的事务处于一种“未决(In-Doubt)”状态。未决事务仅仅出现在非根节点DTC所在的机器。分布式事务采用如下的机制处理未决事务。

当重启后,对于本机的所有未决事务,DTC会向上级DTC发送查询请求,获取每一个事务最终的结果(提交还是中止)。如果上级也不能决定事务的结果,那么请求会沿着事务提交树不断向上(沿着根的方向)发送,直到得到一个明确的答复(不管怎样,位于根节点的全局提交协调器总是清楚事务的结果的)。

此外,当下级DTC向自己发送相同的查询请求的时候,该DTC会讲获取到的结果回复给它们。如果未决事务存在的时间太长,系统管理者可以强制提交或者中止该事务。

4、单阶段提交(SPC: Single-Phase Commit)优化

对于事务最终结果(提交或者中止)的决策者来说,如果它具有了不止一个下级,那么采用基于投票机制的两阶段提交协议唯一选择。但是如果仅仅具有一个唯一的下级呢,这种投票机制就没有必要了。在这种情况下,根节点DTC采用一种优化的协议来完成整个事务的提交,我们称之为单阶段提交(SPC:Single-Phase Commit)。

顾名思义,2PC意味着整个事务提交分成两个阶段,SPC则表示将其所短为一个阶段。整个流程很简单,如果根节点DTC仅仅只有一个登记的事务参与者(本机资源管理器或者下级DTC),而不管这个下级自身具有几个下级,它不会像2PC一样先向下级发送“准备”请求,而是直接发送提交请求,我们将这个请求成为单阶段提交(SPC)请求。接收到SPC请求的参与者,如果是资源管理器,则直接提交本地事务,并将最终结果(成功提交或者失败中止)反馈给这个跟节点DTC。

如果SPC请求的接收者是DTC,那么会根据隶属于自己的下级的数量选择相应的提交策略。具体做法和根节点DTC提交策略的选择方式一样:如果自己具有唯一一个下级,则采用SPC,反之采用2PC。

也就是说,不仅仅是根节点DTC可以选择SPC提交事务,任何具有单一下级的DTC均可以采用SPC。但是,非根节点DTC只有在接收到SPC请求的情况下,才能选择通过SPC提交事务。如图3给出了两颗事务提交树(图中忽略掉资源管理器,每个节点代表DTC),对于左边的树,因为根节点A和下级B均只有一个唯一的下级,所以A和B均采用SPC,C具有两个下级,则采用2PC。而对于右边的树,因为根节点本身具有两个下级,决定了所有的节点均采用2PC,即使是对于只有一个下级的B和C。

图3 DTC对SPC和2PC的选择

我们我们介绍整个Windows平台基于DTC的分布式事务管理模型,接下来的一篇中,我将会详细介绍基于System.Transactions事务的编程。