[续《上篇》]TransactionFlow选项通过TransactionFlowAttribute这个操作契约写入绑定上下文,由事务绑定创建的事务信道获取该选项并以此作为首否对事务实施传播(发送或者接收)的依据。客户端事务信道通过TransactionFormatter对当前事务按照指定的事务处理协议进行格式化,并嵌入出栈消息;通过TransactionFormatter则从入栈消息中提取相应的数据重建事务。这就是事务流转实现的本质。整个WCF事务还有一个重要的步骤需要实现:如何将通过OperationBehaviorAttribute特性标记为TransactionRequired的操作的执行自动纳入到流入的事务之中。接下来,我们就来着重讨论这个问题。
一、 事务的自动登记(Enlistment)
被格式化的事务最终是作为一个消息报头(MessageHeader)的形式被传输的。服务端事务信道接接收到包含有流入事务的消息后,按照指定的协议从相应的报头中获取将被格式化的事务获取出来,并通过TransactionFormatter对事务进行重新创建。被重新创建的事务对象最终以消息属性(MessageProperty)的形式重新放入入栈消息。
这样一个包含有事务对象的消息属性定义在一个类型为TransactionMessageProperty对象之中,TransactionMessageProperty定义如下。只读属性Transaction获取内嵌于消息属性对象的事务,而静态方法Set则将事务作为消息属性植入指定的消息。该消息属性在消息中的Key为TransactionMessageProperty,即类型的名称。
1: public sealed class TransactionMessageProperty
2: {
3: //其他成员
4: public static void Set(Transaction transaction, Message message);
5: public Transaction Transaction { get; }
6: }
WCF运行时根据消息的Action报头定位到相应的操作,如果该操作应用了OperationBehaviorAttribute特性并将TransactionRequired属性设为True,会进行如下的操作:
- 如果入栈消息中包含事务消息属性,则提取事务并基于该事务创建TransactionScope对象。TransactionScope对象的其他一些属性,比如超时时限、隔离级别等采用通过服务行为指定的值。结合前面对System.Transactions事务的介绍,该过程的本质就是创建流入事务的依赖事务,并将创建的依赖事务作为当前的环境事务;
- 如果入栈消息不存在事务属性,则创建一个新的TransactionScope对象。也就是相当于创建一个可提交事务,并将其作为但前的环境事务。
上面的过程是在操作方法被调用之前完成的,并且和操作方法处于相同的线程中。环境事务的存在确保操作方法的执行被纳入到流入的事务或者是一个全新的事务之中。至于事务参与者之间的协调问题,已经不属于WCF体系管辖的范围了,DTC会接收余下的工作。
如果我们将上面的实现通过代码的形式写出来,相信读者的理解会更加深刻。我们以上面演示的转帐操作的实现为例,下面是响应的代码。由于Transfer方法上通过OperationBehaviorAttribute特性将TransactionScopeRequired属性设成True,WCF服务端运行时会自动为我们实现事务登记。
1: [OperationBehavior(TransactionScopeRequired = true)]
2: public void Transfer(string fromAccountId, string toAccountId, double amount)
3: {
4: //转帐操作
5: }
现在我们将OperationBehaviorAttribute特性从Transfer方法中拿掉,通过自己的方式实现事务的自动登记。如果不考虑超时时限和隔离级别等问题,整个实现会如下面的代码所示:
1: public void Transfer(string accountFrom, string accountTo, double amount)
2: {
3: TransactionScope transactionScope = null;
4: if (OperationContext.Current.IncomingMessageProperties.ContainsKey("TransactionMessageProperty"))
5: {
6: TransactionMessageProperty transactionMessageProperty = (TransactionMessageProperty)OperationContext.Current.IncomingMessageProperties["TransactionMessageProperty"];
7: transactionScope = new TransactionScope(transactionMessageProperty.Transaction);
8: }
9: else
10: {
11: transactionScope = new TransactionScope();
12: }
13: try
14: {
15: //转帐操作
16: transactionScope.Complete();
17: }
18: finally
19: {
20: transactionScope.Dispose();
21: }
22: }
二、OleTx提升(OleTx Upgrade)机制
在《一步步创建一个完整的分布式事务应用》的实例演示中我们谈到,即使我们将绑定采用的事务处理协议设置成WS-AT,并且在DTC中对WS-AT进行了正确的设置,WCF运行时仍有可能采用OleTx协议进行事务处理,这就是将要介绍的OleTx提升机制。
OleTx是Windows平台下默认的分布式事务协议,它采用安全RPC(Secure RPC: SRPC)协议进行通信,并采用二进制编码,具有最好的性能优势。对于WCF事务来说,即使我们显式地将WS-AT设置成绑定采用的事务协议,如果DTC发现当前的事务应用场景仍然能够采用OleTx进行处理,会自动将WS-AT协议提升到OleTx协议,这就是OleTx提升机制。
我们将我们的视线再次移向上面基于TransactionFormatter的例子,通过分析包含有格式化事务数据的三种基于不同事务协议的SOAP消息的结构,我们会发现基于OleTx的所有信息均包含在基于WS-AT的消息之中。实际上,OleTx需要的仅仅是事务的传播令牌(Propagation Token)。也就是说,对于WS-AT协调上下文接收方的DTC是可以通过OleTx进行事务处理的。
在默认的情况下,OleTx提升机制自动生效。我们可以通过修改相应的注册表项对OleTx提升进行开启和关闭,该注册表项就是我们上面提到的HKLM\SOFTWARE\Microsoft\WSAT\3.0\OleTxUpgradeEnabled。接下来我们将介绍在不同的应用场景下将绑定的事务类型设置成WS-AT,分布式事务的实现真正采用的实现方式:
- 场景1:WCF客户端和服务端不部属于同一台主机,不论是OleTxUpgradeEnabled作何设置,客户端和服务端均采用SRPC与DTC进行通信。实际上,无论对于何种场景,事务参与者与本地DTC之间通信的方式总是SRPC。
- 场景2: WCF客户端和服务端部属于不同的主机,在没有设置OleTxUpgradeEnabled或者OleTxUpgradeEnabled=1的境况下,DTC之间采用OleTx进行事务处理,通信方式为SRPC。
- 场景3: WCF客户端和服务端部属于不同的主机,将OleTxUpgradeEnabled设置为0的境况下,DTC之间采用WS-AT进行事务处理;
- 场景4:WCF客户端调用另一平台服务,DTC和服务所在主机的事务管理器(TM:Transaction Manager)采用WS-AT进行事务处理;其他平台客户端调用WCF服务,DTC和TM之间也采用WS-AT进行事务处理。
上述的4个场景如下图所示:
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。