一、事务概述
维护系统一致性和正确地处理错误恢复挑战的最佳方式是使用事务。
一个事务就是一个复杂操作的集合,这个集合中任何一个操作的失败都会引起整个集合的失败。
尽管在事务进行时系统被允许暂时出于不一致的状态,但是一旦事务结束,系统一定会处于一直的状态。
事务资源
事务编程需要资源(如数据库或消息队列)一起协作,这类资源要能够参与事务并回滚在事务中发生的变化。
一般情况下,我们必须通知这些希望执行事务性工作的资源,这个行为称为在事务中登记资源。一些资源支持自动登记,也就是说它们能够检测到自身是否被事务访问,并完成登记。资源呢一旦被登记,我们就能够对它执行操作。如果没有错误发生,要求提交资源的所有状态发生改变;剐有错误发生,要求回滚资源的状态的所有变化。
在一个事务处理中,不要访问非事务的资源很关键(如文件系统),因为事务一旦被中止,是不会回滚这些非事务资源上的改变。
事务属性
当在面向服务的应用程序中使用事务时,必须遵守四个核心属性,也就是ACID:原子性(Atomic)、一致性(Consistent)、隔离性(Isolated)和持久性(Durable)。
原子性
为了是事务具有原子性,当一个事务结束时,必须把对资源状态已经做的所有改变视为一个原子的,不可分割的操作。
当事务结束时,事务是不容许遗留任何工作在后台处理,因为这些操作违反了原子性。所有事务产生的操作必须包含在事务中。
事务作为一个整体,要么成功,要么失败。
一致性
一致性意味着事务必须是系统处于一个一致的状态。一致性和原子性不同。即使状态的所有改变被作为一个原子操作提交了,事务也必须保证所有的改变都是一致的。
事务必须做到的就是让系统从一个一致的状态转移到另一个一致的状态。
隔离性
隔离性意味着没有其它的实体在事务过程中能够看到资源的状态,因为中间状态很可能是不一致的。即使中间状态是一致的,事务依然能够中止,所有的改变也可以被回滚。
参与事务的资源必须对事务所访问的数据枷锁,以防止任何其它代码的访问,同时必须在事务提交或中止取消对数据的所保护,以便其它代码可以访问。
持久性
持久性的意思是一旦成功提交,基于持久化资源(比如数据库)的数据将会被持久化,对数据的改变是永久性的。
二、事务管理
WCF服务能够直接处理事务性的资源,并通过显式地使用编程模型(如ADO.NET)来管理事务。如下,使用这个模型,可以显式地启动和管理事务。
[ServiceContract] interface IMyContract { [OperationContract] void MyMethod(); }</span><span style="color: #0000ff">class</span><span style="color: #000000"> MyService : IMyContract { </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> MyMethod() { </span><span style="color: #0000ff">string</span> connectionString = <span style="color: #800000">"</span><span style="color: #800000">....</span><span style="color: #800000">"</span><span style="color: #000000">; </span><span style="color: #0000ff">using</span> (IDbConnection connection=<span style="color: #0000ff">new</span><span style="color: #000000"> SqlConnection(connectionString)) { connection.Open(); </span><span style="color: #0000ff">using</span> (IDbCommand command=<span style="color: #0000ff">new</span><span style="color: #000000"> SqlCommand()) { command.Connection </span>=<span style="color: #000000"> connection; </span><span style="color: #0000ff">using</span> (IDbTransaction transaction =<span style="color: #000000">connection.BeginTransaction()) { command.Transaction </span>=<span style="color: #000000"> transaction; </span><span style="color: #0000ff">try</span><span style="color: #000000"> { </span><span style="color: #008000">//</span><span style="color: #008000">这里操作数据库</span> transaction.Commit(); <span style="color: #008000">//</span><span style="color: #008000">提交事务</span>
}
catch (Exception)
{
transaction.Rollback(); //回滚事务
throw;
}
}
}
}
}
}事务性管理的挑战
虽然编程模型十分简单,不需要服务执行任何事务,但是这个最适合单个客户端调用单个数据库,服务会启动并管理事务,如下所示:
但是,一个面向服务的应用系统,客户端一次于多个服务交互,并且涉及到多个资源,如下图所示
现在问题是,哪一个参与的服务负责发起事务并管理资源?如果大家都服务的话,就会以多个事务结束;如果服务里包含过多的业务逻辑将会引起服务、资源之间紧密耦合的问题;再说哪个服务服负责提交或回滚事务呢;这个服务如何直到其它事务的 情况呢……
一种可行的方法是,通过为事务协调增加代码可以把客户端与服务结合起来,但是这个比较脆弱,业务流程或服务数量变化都会导致这个方法失效。
三、分布式事务
刚才介绍的事务类型 称为分布式事务。一个分布式事务包含两个或多个独立的服务(通常在不同的执行上下文里),或者一个服务包含多个事务资源。对于一个分布式事务,需要依赖两阶段提交协议和一个事务管理器。事务管理器是管理事务的第三方,由它来负责客户端和服务端两阶段提交协议。
四 、两阶段提交协议
为了克服分布式事务的复杂性,事务管理器使用一个称作两阶段提交协议的事务管理协议来决定事务提交或回滚。两阶段提交协议在分布式事务中强调原子性与一致性。让WCF支持多客户端、服务以及资源的分布式事务。当事务工作时,事务管理器会进行监督。当有新的服务加入事务,以及访问到的资源都会与这个事务关联。服务执行业务逻辑资源,资源记录事务内所做的修改。在事务期间,所有的服务(参与到事务里的客户端)必须通过投票决定它们是否提交事务或终止事务。
当事务结束时,事务管理器会检查参与事务投票的所有服务。如果任何一个服务或客户端投票终止事务,事务就不会提交:所有在事务期间对资源的修改都会被抛弃。如果所有的参与者都投票提交事务,那么就会启动两阶段提交协议:
第一阶段,事务管理器询问参与到事务里的所有资源是否对于提交事务期间的修改还有什么意见。注意,事务管理器不会命令资源提交修改,而只是询问它们的意见。第一阶段结束时,事务管理器会综合所有的资源投票结果。
第二阶段,根据投票结果来执行。如果所有的资源都投票提交事务,那么事务管理器会告诉所有的资源提交修改。但是,如果任何一个资源没有提交修改,那么资源管理器会告诉其余的资源回滚修改,然后终止事务,并把系统恢复到事务之前的状态。
五、资源管理器
资源管理器(RM)是由某个事务管理器管理的、支持原子操作和两阶段提交协议的资源。这个资源必须可以检测自己是否被包含进了事务,而且是否有且只有一次的自动那个加入事务列表。RM可以处理持久化和非持久化资源,比如,事务性整数、字符串和集合。虽然RM必须支持两阶段提交协议,但是,他可以选择使用优化协议。这个协议称为一阶段提交协议,事务管理器(TM)会一次性通知事务管理器执行的结果。
资源管理器在整个事务模型中主要承担如下几种职能:
- 帮助应用实现对目标资源的操作;
- 注册到相应的事务管理器,以便事务回滚得时候可以从事务管理器中接收到恢复请求,实现对数据的恢复;
- 向相应的事务管理器报告本地事务的结果;