最近开始WCF相关知识的学习,虽然实际工作中使用公司自己的一套SOA系统,但微软的一套服务架构还是具有很大的参考意义。除了WCF的一些基础使用,相对比较复杂的内容有分布式的事务和通信的安全等,不过基本都和WS-协议簇相关联。为了引出WCF中的事务处理,今天优先介绍Windows下的事务处理模型,说实话用了很多年的TransactionScope,其实我一直都不知道它到底是如何运作的,回想Java中不管是EJB还是Spring也有和这部分类似的实现。今天通过学习蒋金楠老师的WCF服务框架解析来了解这部分的原理。

首先回忆事务的概念和相关应用机制。事务具有ACID(原子性,一致性,隔离性,持久性)特性,主要的应用场景比如银行转账,大部分的数据库都支持事务操作(SQL Server,Oracle,DB2,MySQL-InnoDB)。事务的实现最常见的有在SQL语句中使用事务BEGIN TRANSACTION, COMMIT TRANSACTION, ROLLBACK TRANSACTION,还可以通过ADO.NET的DbTransaction对象来控制事务,但这些只能支持单个链接的本地事务,而不能支持分布式事务。但实际的项目中,事务的参与者往往不会分布在同一个网络节点,有时是不同类型的事务资源(不仅仅是数据库,也可以是文件系统,注册表等)。分布式事务的典型业务场景有以下三类:将多个资源的访问纳入同一事务;将多个服务纳入同一事务(涉及事务在服务间的流转);将多个资源和服务纳入同一事务(如下图所示)。

接下来进入核心内容Windows下的事务处理模型,事务处理模型一般包括三种角色:

应用(也包括服务、组件等),资源管理器RM(管理具体事务型资源的软件程序),事务管理器TM(管理整个事务的中间件程序)。事务是为了将一组相关操作作为一个不可分割的整体来执行,保证数据的一致性,实际上就是为应用服务的。应用主要负责开始事务&事务的封送(Marshaling)和传播(Propagation)、提交事务。

事务型资源的存取需要通过资源管理器,按照目标资源是否可以被持久化,相应的资源管理器分为:持久化(Durable)资源管理器,例如数据库管理器和小心队列,当事务回滚时具有可恢复性;易失(Volatile)资源管理器用于管理类似内存的资源,不具可恢复性。其主要职责是帮助应用实现对目标资源的操作、注册到相应的事务管理器便于回滚时接受恢复请求、向事务管理器报告本地事务的结果。事务管理器

事务管理器是整个模型的枢纽,协调所有的事务参与者,提供事务的开始、提交和回滚服务。Windows提供了三种不同的事务管理器,包括轻量级事务管理器LTM、内核事务管理器KTM和分布式事务协调器DTC。不过从这儿也可以看出事务服务并不具有跨平台特性,因而这部分内容主要作为参考学习,重在理解原理。轻量级事务管理器和内核事务管理器分别负责SQLSERVER和Windows文件&注册表,高效但不支持分布式。分布式事务协调器用于管理跨边界的分布式事务,支持Ole-Tx和WS-AT协议,每一台Windows计算机都具有唯一的DTC用于管理本地所有的资源管理器,支持分布式但效率较低。需要注意的是,事务开始默认LTM作为事务管理器,在其进行的过程中,根据实际的情形会进行事务提升(Transaction Promotion)。例如,涉及到注册表的读写就会升级到KTM,涉及到跨域的封送就会提升到DTC。

那么我们不经要问,分布式事务到底是如何实现的呢?其最关键的两个概念分别是事务登记&事务提交树(Transaction Commit Tree)和两阶段提交协议,接下来分别介绍这两个概念。

事务登记(Transaction Enlist)的目的在建立事务参与者之间的关系,促进相互间的协作,整个流程如下图所示:

以上的事务涉及两台机器,事务由ServiceA开启,并将其作为当前执行上下文的环境事务(Ambient Transaction)。当ServiceA调用本机资源管理器(如SQLSERVER)时,会将该RM纳入到本事务中。资源管理器RM向本机的DTC进行事务登记,从而DTC于RM建立起上下级关系。当ServiceA在调用ServiceB时,会将当前事务信息(分布式事务ID和本机DTC信息)封送,Service接到后取出信息重建事务,并将其设置为环境事务具有与原事务相同的ID。同时,ServiceB根据得到的MachineA的DTC消息,让本机DTC对MachineA的DTC进行事务登记,使得两台机器DTC建立上下级关系,之后将RM也纳入该DTC管理。在登记流程结束后就形成了如下事务提交树:

在分布式环境下,事务提交需要保证在操作成功时,所有需要持久化的数据被相应资源管理器RM写入目标资源,而失败时所有RM中数据要恢复到原始状态。为了实现这一目标,分布式事务提交需要采用"两阶段提交"协议,接下来详细介绍这两个阶段:

第一阶段--准备阶段,根节点的DTC向所有事务参与者发起请求,要求他们对本地事务的结果进行投票,如上图所示递归的进行消息传播,相应节点反馈就绪、只读、终止等投票类型。若所有投票结果为就绪和只读就代表提交,如果有任何一个为终止则代表终止提交。同时可以设置超时时限,若超过此时限整个事务进行回滚。

第二阶段—提交或者回滚,根节点DTC根据投票结果对整个事务发起提交或者终止操作,使用和第一阶段相同的消息传播方式完成操作。当遇到事务参与者在完成第一阶段投票后网络断开等异常情况时,该子事务处于"未决态"。再重启此事务后,该DTC会向上级询问最终结果,如果上级不能确认则继续向根节点传播,直到得到答复,若时间太长系统管理者可以强制提交或终止事务。

此外,为了提高提交性能,协议会根据节点所具有的下级节点数决定是否选用单阶段提交协议,就是在节点只有一个唯一下级时直接发起提交。

在完成了原理的剖析后,进入实际的应用,这也是与工作息息相关的部分。WCF基于DTC的Windows事务架构为基础,提供一个完善的分布式事务解决方案。提供了System.Transactions.Transaction的命令式编程模型和System.Transactions.TransactionScope的声明式编程方式。

Transaction类时可以序列化封送的,通过EnlistDurable和EnlistVolatile等方法将资源管理器登记到当前事务,构建事务提交树。Currrent属性表示当前的环境事务(Ambient),其存储在当前线程的TLS中。TransactionInformation属性表示事务的基本信息,包括创建时间、状态、本地标识和分布式标识。并通过IsolationLevel表示隔离级别,使用Clone和Rollback方法克隆事务和回滚事务,需要注意的是事务的开始和结束需要同一个事务来完成,我们把这种事务称为可提交事务(Commitable Transaction),而其他的相关事务被称为依赖事务(DependentTransaction)。可提交事务通过TransactionOptions结构体设置超时时间和隔离级别,也可以通过如下配置设定。

 <system.transactions>
 <defaultSettings timeout="00:01:00"/>
 <machineSettings maxTimeout="00:10:00"/>
 </system.transactions>

可提交事务通过Commit方法和异步Begin/EndCommit方法提交事务,例子如下所示:

 private static void Transfer(string accountFrom, string accountTo, double amount) {
 Transaction originalTransaction = Transaction.Current;
 CommittableTransaction ct = new CommittableTransaction();
 try
 {
 Transaction.Current = ct;
 Withdraw(accountFrom, amount);
 Deposit(accountTo, amount);
 ct.Commit();
 }
 catch
 {
 ct.Rollback();
 throw;
 }
 finally {
 Transaction.Current = originalTransaction;
 ct.Dispose();
 }
 }

在Transaction类中存在一个DepedentClone的方法,该方法用于基于当前事务创建其所对应的依赖事务,也就是其所辖的子事务,将当前线程的环境事务传递到新的事务中。接下来的代码演示了通过依赖事务采用异步方式进行银行转账。需要注意的是,由于在调用DependentClone时指定的Options参数为BlockCommitUntilComplete,所以主线程在提交事务时,如果依赖事务未结束会一直等待到超时。还有一个关于事务型方法的实现也很有意思,大家有兴趣可以去看蒋老师的原著。

 private static void Transfer(string accountFrom, string accountTo, double amount)
 {
 Transaction originalTransaction = Transaction.Current;
 CommittableTransaction ct = new CommittableTransaction();
 try
 {
 Transaction.Current = ct;
 ThreadPool.QueueUserWorkItem(state => {
 Transaction.Current = state as DependentTransaction;
 try
 {
 Withdraw(accountFrom, amount);
 Deposit(accountTo, amount);
 (state as DependentTransaction).Complete();
 }
 catch (Exception ex) { Transaction.Current.Rollback(ex); }
 finally {
 (state as DependentTransaction).Dispose();
 Transaction.Current = null;
 }
 }, Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete));
 ct.Commit();
 }
 catch
 {
 ct.Rollback();
 throw;
 }
 finally
 {
 Transaction.Current = originalTransaction;
 ct.Dispose();
 }
 }

最后介绍实际工作中最常见的TransactionScope类,如之前提到的事务型方法就有如下简易实现。

 static void InvokeInTransaction(Action action)
 {
 using (TransactionScope ts = new TransactionScope())
 {
 action();
 ts.Complete();
 }
 }

需要注意的是TransactionScopeOption的设置,有如下三种类型:Required,表示如果存在环境事务则使用,否则进入范围前创建新的事务;RequiresNew,表示总是在该范围创建新事物;Suppress,表示屏蔽环境事务,即所有操作均在无环境事务的情况下执行。这个J2EE Spring中的REQUIRED 、REQUIRES_NEW   、 NOT_SUPPORTED很相似,不过对于J2EE与DTC相类似的机制个人还未涉猎,之后有机会再和大家分享了。

注:本文主要供自己学习,不妥之处望见谅。

参考资料:

[1]蒋金楠. WCF全面解析[M]. 上海:电子工业出版社, 2012.

快速入门系列--WCF--05事务的更多相关文章

  1. 快速入门系列--WebAPI--01基础

    ASP.NET MVC和WebAPI已经是.NET Web部分的主流,刚开始时两个公用同一个管道,之后为了更加的轻量化(WebAPI是对WCF Restful的轻量化),WebAPI使用了新的管道,因 ...

  2. 快速入门系列--WebAPI--03框架你值得拥有

    接下来进入的是俺在ASP.NET学习中最重要的WebAPI部分,在现在流行的互联网场景下,WebAPI可以和HTML5.单页应用程序SPA等技术和理念很好的结合在一起.所谓ASP.NET WebAPI ...

  3. 快速入门系列--WebAPI--04在老版本MVC4下的调整

    WebAPI是建立在MVC和WCF的基础上的,原来微软老是喜欢封装的很多,这次终于愿意将http编程模型的相关细节暴露给我们了.在之前的介绍中,基本上都基于.NET 4.5之后版本,其System.N ...

  4. 快速入门系列--MVC--01概述

    虽然使用MVC已经不少年,相关技术的学习进行了多次,但是很多技术思路的理解其实都不够深入.其实就在MVC框架中有很多设计模式和设计思路的体现,例如DependencyResolver类就包含我们常见的 ...

  5. [转]快速入门系列--WebAPI--01基础

    本文转自:http://www.cnblogs.com/wanliwang01/p/aspnet_webapi_base01.html ASP.NET MVC和WebAPI已经是.NET Web部分的 ...

  6. 快速入门系列--MVC--02路由

    现在补上URL路由的学习,至于蒋老师自建的MVC小引擎和相关案例就放在论文提交后再实践咯.通过ASP.NET的路由系统,可以完成请求URL与物理文件的分离,其优点是:灵活性.可读性.SEO优化.接下来 ...

  7. 快速入门系列--MVC--07与HTML5移动开发的结合

    现在移动互联网的盛行,跨平台并兼容不同设备的HTML5越来越盛行,很多公司都在将自己过去的非HTML5网站应用渐进式的转化为HTML5应用,使得一套代码可以兼容不同的物理终端设备和浏览器,极大的提高了 ...

  8. WPF快速入门系列(4)——深入解析WPF绑定

    一.引言 WPF绑定使得原本需要多行代码实现的功能,现在只需要简单的XAML代码就可以完成之前多行后台代码实现的功能.WPF绑定可以理解为一种关系,该关系告诉WPF从一个源对象提取一些信息,并将这些信 ...

  9. WPF快速入门系列(1)——WPF布局概览

    一.引言 关于WPF早在一年前就已经看过<深入浅出WPF>这本书,当时看完之后由于没有做笔记,以至于我现在又重新捡起来并记录下学习的过程,本系列将是一个WPF快速入门系列,主要介绍WPF中 ...

  10. Qt快速入门系列教程目录

    Qt快速入门系列教程目录

随机推荐

  1. 《数据结构与算法Python语言描述》习题第二章第二题(python版)

    ADT Date: #定义日期对象的抽象数据类型 Date(self, int year, int month, int day) #构造表示year/month/day的对象 difference( ...

  2. java初学者应掌握的30个基本概念

    核心提示:OOP中唯一关系的是对象的接口是什么,就像计算机的销售商她不管电源内部结构 是怎样的,他只关系能否给你提供电就行了,也就是只要知道can or not而不是how and why. 基本概念 ...

  3. shell实现https登录

    服务端提供了两个api: 一个是用于用户认证,因为要传输密钥,所以用了https方式  如何在服务端配置https请见另外一个博文 https://192.168.1.190:8443/api/aut ...

  4. 图论$\cdot$最短路问题

    Dijkstra单源最短路径算法 Dijkstra可以计算出发点到每个点的最短路,及单源最短路径(SSSP).这一特点使得Dijkstra常常用来进行其他算法的预处理.用Dijkstra算法计算最短路 ...

  5. ios开发FMDB导入SQLCipher加密数据库

    转:http://www.2cto.com/kf/201407/315727.html [iOS]FMDB/SQLCipher数据库加解密,迁移

  6. mysql一个表中多个字段对应另一个表的id如何查询?

    比如有如下2个表 a 和baaID b1ID b2ID b3ID1 1 3 52 2 4 6bbID bCon1 苹果2 香蕉3 国内4 国外5 出口6 进口其中a表中的b1ID,b2ID,b3ID都 ...

  7. c++读取ccbi

    loader类文件: 需要定义CCB_STATIC_NEW_AUTORELEASE_OBJECT_METHOD(ButtonTestLayerLoader, loader); 这个宏定义是定义静态的l ...

  8. Android Socket 遇到的Soure Not Find 错误

    参考: http://blog.csdn.net/brokge/article/details/8543145 http://blog.csdn.net/mad1989/article/details ...

  9. VMware6.0-vCenter的安装准备及安装

    由于6.0的VCSA安装需要跳板主机来辅助,而在5.5时时可用用OVA导入模式安装的. 首先安装跳板插件 安装完成后,点击setup链接. 设置 Single Sign-On (SSO),将root和 ...

  10. Java中关于HashMap源码的研究

    1.基础知识 1.数组 数组存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二分查找时间复杂度小,为O(1):数组的特点是:寻址容易,插入和删除困难. 2.链表 链表存储区间离散,占用内存比 ...