WCF 技术剖析之三十三:你是否了解WCF事务框架体系内部的工作机制?[下篇]
[续《上篇》]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提升进行开启和关闭,该注册表项就是我们上面提到的HKLMSOFTWAREMicrosoftWSAT3.0OleTxUpgradeEnabled。接下来我们将介绍在不同的应用场景下将绑定的事务类型设置成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个场景如下图所示:
- 文档数据库系统CouchDB
- Web 单点登录系统
- IDC+BIM,或将带来数据中心新革命
- CSS 代码的书写规范、顺序
- MSBuild入门(续)
- 续:WordPress 文章图片部署真正的懒加载(Lazy Load)
- Windows 下的安装phpMoAdmin
- 实用代码-JavaScript实用小函数一枚(深入对象取值)
- SQL Server 2008 压缩
- 如何在程序中加入Growl通知
- WordPress 设置与调用 Cookie 的相关代码
- MSBuild入门
- HTTP Basic Authentication验证WCF Data Service
- 移除WordPress 仪表盘首页的“插件”“其它WordPress 新闻”小工具
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 什么时候触发GC
- 找找数学上的规律——LeetCode题目11:盛最多水的容器
- Java学习笔记, 不断更新
- 这题真是送分——LeetCode题目12:整数转罗马数字
- 该了解一波了!零基础入门Nginx
- 轻松一刻——LeetCode题目13:罗马数字转整数
- 动动手——LeetCode题目14:最长公共前缀
- LeetCode题目15:三数之和
- 三数之和姊妹题——LeetCode题目16:最接近的三数之和
- 组合问题——LeetCode题目17:电话号码的字母组合
- Python读取PDF文档并翻译
- n数之和题目要类比——LeetCode题目18:四数之和
- SpringBoot使用MySQL访问数据
- MySQL数据库与JDBC编程
- 自动删除QQ空间指定好友的留言