使用 TIP (Transaction Internet Protocol) 将 Web 服务纳入 COM+ 分布式事务






4.13/5 (4投票s)
2006年2月8日
7分钟阅读

54028

438
演示了如何使用 TIP 与 COM+ 分布式事务中的 Web 服务进行通信。
引言
COM+ 分布式事务可以通过两阶段提交将多个不同的数据源合并到一个事务中。通过 TIP(事务 Internet 协议)URL,运行在不同机器或不同进程中的代码也可以参与分布式事务。
Notice
TIP 虽然在 .NET 1.1 和 .NET 2.0 中仍然可用,但它是一项老旧的技术。随着 Windows Vista 的即将发布,Microsoft 将逐步停止对其的支持。如果您仍然对事务性 Web 服务感兴趣,有一个替代方案。请查看我关于 Vista 的 WS-AtomicTransaction 的新文章 这里[^]。随着越来越多的公司采用 WS-AtomicTransaction 标准,这将是未来分布式事务跨 Web 服务的实现方式。
背景
分布式事务是我最喜欢使用 COM+ 的部分。通过开启一个分布式事务,我可以将许多不同的数据库、消息队列、文件、电子邮件以及几乎所有我想到的东西纳入同一个事务中。两阶段提交告诉我,它们要么全部成功,要么全部回滚。如果服务器发生故障,事务还可以从中断处重新启动。
TIP 实际上是一个文档齐全、标准化的协议。它允许多个应用程序就事务信息进行通信。对于普通的分布式事务,DTC(分布式事务协调器)会与您的数据源通信以控制两阶段提交。同一台机器上的其他应用程序只要能获取到事务对象,就可以参与该事务。但是,运行在不同机器上的应用程序需要更好的解决方案。这就是 TIP 的用途。
本文借鉴了我上一篇 文章 的很多内容。在那篇文章中,我使用了 NHibernate 并将其纳入 COM+ 分布式事务。本文也需要作为示例进行一些数据访问,所以我决定将 NHibernate 的内容复制过来。您不必使用 NHibernate 即可采用本文所述技术。
使用代码
我的代码中有几点您需要注意
- 代码中包含一个名为 ComPlusTipWebService 的 Web 服务项目。在打开解决方案之前,请确保在 IIS 中将其配置为同名的虚拟目录。
- COM+ 项目有一个生成后事件。生成后事件使用 regsvcs 来注销和重新注册 COM+ DLL。这会稍微增加编译时间,但省去了我手动操作的麻烦。为了让 regsvcs 工作,我将框架目录添加到了我的路径(C:\Windows\Microsoft.NET\Framework\v1.1.4322)。一如既往,如果您更改了路径变量,则必须重新启动 Visual Studio。
- 我还创建了一个 NUnit 测试启动器项目来运行实际的测试。
- 在测试启动器项目的生成后事件中,我将 App.Config 复制到目标目录,并将其命名为 DLL 名称加上 ".config"。这样 NUnit 就可以找到它了。
- 通常,我会将测试启动器项目设置为启动项目。要让 NUnit 启动项目,请打开项目属性窗口,从左侧选择“配置属性”->“调试”。将“调试模式”设置为“程序”,然后单击“应用”。对于“启动应用程序”,找到 NUnit GUI 可执行文件(C:\Program Files\NUnit 2.2\bin\nunit-gui.exe)。对于命令行参数,输入 DLL 的文件名(CPNTestHarness.dll)。
- 我包含了一个 SQL 脚本来创建示例中使用的表。将这些表添加到您的数据库中,并更改 Web 服务项目的 Web.Config 中的连接字符串。
- 您可能已经注意到我设置 NHibernate 配置的奇怪方式。我没有使用 App.Config 来设置它,而是通过代码来设置,并从 App.Config 中获取连接字符串。这只是个人偏好。我通常发现在企业环境中,App.Config 无法胜任工作,通常会从 Microsoft 企业库中获取配置设置。所以我只是快速更改了代码以适应这个示例。
为了使事务从客户端到服务器端正常工作,运行它的计算机需要进行一些更改。为了传输 DTC 事务信息,会使用 TIP URL。TIP 在 Windows 2000 上默认启用,但在 Windows XP 和 Server 2003 上默认禁用。此外,Windows XP 似乎有一个服务包会普遍禁用 DTC 的网络访问。要重新启用它,请使用 EnableTip.reg 注册表文件来修复键值。它查看的是以下内容:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSDTC\Security
以下键值设置为 1
- NetworkDtcAccess
- NetworkDtcAccessTip
- NetworkDtcAccessTransactions
设置完这些键值后,通过打开命令提示符并键入以下命令来重新启动 DTC 服务:
net stop msdtc
net start msdtc
ITipTransaction 接口
启动事务后,ContextUtil
类允许您访问 Transaction
属性。此属性的值是一个描述当前 COM+ 事务的对象。您可以将此对象强制转换为 System.EnterpriseServices.ITransaction
接口以获取一些信息。不过,通常不需要这样做,因为 EnterpriseServices 库已经为您处理了大部分工作。
除了能够将事务对象强制转换为 ITransaction
之外,您还可以将其强制转换为另一个名为 ITipTransaction
的接口。此接口不存在于 System.EnterpriseServices
库中。这意味着您必须自己创建一个来匹配 COM 接口。这是代码:
using System;
using System.Runtime.InteropServices;
namespace Common
{
/// <summary>
/// An interface that matches the COM+ ITIPTransaction
/// interface. We can use this to pull out the
/// TIP URL and send it to the server.
/// </summary>
[ComImport]
[Guid("17CF72D0-BAC5-11d1-B1BF-00C04FC2F3EF")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITipTransaction
{
/// <summary>
/// Push
/// </summary>
/// <param name="i_pszRemoteTmUrl"></param>
/// <param name="o_ppszRemoteTxUrl"></param>
void Push([MarshalAs(UnmanagedType.LPStr)]
string i_pszRemoteTmUrl,
[MarshalAs(UnmanagedType.LPStr)] out string
o_ppszRemoteTxUrl);
/// <summary>
/// Gets the transaction URL.
/// </summary>
/// <param name="o_ppzLocalTxUrl"></param>
void GetTransactionUrl([MarshalAs(UnmanagedType.LPStr)] out string
o_ppzLocalTxUrl);
}
}
该接口非常简单。我们最感兴趣的方法是 GetTransactionUrl
,因为 TIP URL 实际上是我们所需要的一切。有了我们创建的接口,要获取当前打开事务的 TIP URL,只需要这样做:
ITipTransaction trans = (ITipTransaction)ContextUtil.Transaction;
string tipUrl = "";
trans.GetTransactionUrl(out tipUrl);
return tipUrl;
就是这样!现在我们有了 TIP URL,就可以将其传递给其他应用程序,以便它们能够参与分布式事务。参与过程也同样简单。假设我有一个 ServicedComponent
,它看起来像这样:
[Transaction(TransactionOption.Supported)]
public class OrmManager : ServicedComponent { ... }
此类与我的数据源通信。我可以在 TIP 事务的上下文中创建此类型的对象。为此,我使用了 System.EnterpriseServices.BYOT
(Bring Your Own Transaction,即自带事务)类:
OrmManager om = null;
om = BYOT.CreateWithTipTransaction(tipUrl,
typeof(OrmManager)) as OrmManager;
代码工作原理
此代码实际上是我上一篇 文章 的扩展。因此,如果 NHibernate 或测试启动器的用法中有任何令人困惑之处,请参阅该文章。基本上,在那段代码中我有一个 TransactionController
类,它允许我对特定的 NHibernate 类执行 Save、Delete 和 GetAll 操作。我只实现了执行测试所需的方法。上一篇文章中的 NUnit 测试启动器运行了两个测试,以确保分布式事务正常工作。本文中的代码差别不大。最大的区别在于持久化是通过 Web 服务完成的。因此,它完全超出了测试启动器的应用程序域。我这样做是为了强调在面向服务的体系结构 (SOA) 中使用 TIP 可能产生一些非常有趣的结果。
摘要
我非常喜欢事务性代码。让数据库、消息队列、主机以及所有第三方软件在同一个事务下通信将是一件令人兴奋的事情。特别是在面向服务的体系结构中。然而,我并没有看到很多人使用 COM+ 来做到这一点。我想这有很多原因,其中最大的可能是 DTC 协调所有事务的速度很慢。相反,很多程序员似乎会选择编写某种专有的事务处理,或者干脆完全放弃。
历史
- 1.0: 2006-02-08: 初始修订。