COM+ 与 .NET - 实用的方法 - 第一部分






4.75/5 (23投票s)
2004 年 3 月 3 日
21分钟阅读

136460
审视 COM+ 和 .NET
摘要
本文实际上由三个主要部分组成。第一部分处理使用 COM+ 的所有方面。阅读完本部分后,您将了解如何在 COM+ 中托管程序集的选项、每个选项对 ASP.NET 应用程序性能的影响,以及选择特定 COM+ 托管选项会带来哪些限制。
第二部分介绍了您的应用程序可以从 COM+ 获得的所有管理优势。本部分包括有关使用 COM+ 提高可用性和稳定性的详细信息,以及如何监视和使用监视数据来快速轻松地检测问题源。
第三部分介绍了使用 COM+ 功能可以轻松实现的日常编程任务。COM+ 包含许多可以减少您为完成常见编程任务可能投入的代码行数和时间的特性。本部分将概述这些编程任务,并演示如何使用 COM+ 来实现它们。
引言
自 Windows 2000 以来,COM+ 在我们的 WEB 服务器上已经存在,但并不活跃。COM+ 甚至在 XP 和 Windows 2003 操作系统中得到了升级到 1.5 版本。COM+ 对 DNA 程序很有用,但在 .Net 时代似乎已经失去了光彩。大多数程序员使用 COM+ 来管理数据库事务,但 COM+ 提供了许多其他选项,可用于创建更具可伸缩性和可用性的应用程序,并简化编程任务。本文将通过解释和演示何时以及如何将 COM+ 与 .Net 一起使用,采取一种实用的方法来研究 COM+(1.0 和 1.5)。
定义问题
COM+ 这个名字是其最大的缺点之一。COM 这个词的组合误导了许多程序员,让他们认为 COM+ 基于 COM,并阻止在新品牌 .NET WEB 应用程序中使用旧技术。虽然在某些情况下 COM+ 确实基于 COM,但利用其能力可以极大地促进 WEB 应用程序和服务器的发展。.Net 的创建者考虑了使用 COM 访问 COM+ 应用程序的后果,并以一种可以访问 COM+ 中的 .Net 组件而无需使用 COM 的方式创建了 CLR。这种架构使我们能够使用 COM+ 服务,如排队组件、松耦合事件、池化、JITA(即时激活)、SWC(无组件服务)等,来简化编程任务。
在本文中,我将展示如何将 .Net 组件注册到 COM+ 中,以及这种注册对性能和限制有什么影响。在了解使用 COM+ 的后果后,我们将看到 COM+ 如何提高应用程序和 Web 服务器的可用性和稳定性。继续采用实用的方法,我们将看到 COM+ 的特性如何简化我们在创建 WEB 应用程序时面临的日常任务。
本文讨论 COM+ 1.0 和 1.5。在提到 COM+ 时,我指的是 COM+ 1.0 和 1.5 中都存在的特性。在明确提到 COM+ 1.5 时,指的是仅存在于 COM+ 1.5 版本(Windows XP 或 .Net 服务器)中的特性。
文章结构
- COM+ 应用程序类型及其对应用程序性能的影响
- 性能
- 访问和限制
- 使应用程序尽可能可伸缩
- 最大隔离
- 组件监视和快速问题解决
- 应用程序回收
- 伸缩对象
- 进程转储
- 允许不同版本的 COM+ 应用程序同时运行
- 展望美好未来
- 应用安全
- 提高服务器产出和应用程序收入。
- 监视和控制许可证使用。
- 在应用程序状态更改时通知他人
- 在无需培养 COM+ 树的情况下采摘 COM+ 的果实。
- 从 COM+ 服务器应用程序通知 ASP.NET。
- 事务,仅供记录。
(a) COM+ 应用程序类型及其对应用程序性能的影响
COM+ 可以通过两种方式托管 .Net 组件:库应用程序和服务器应用程序。库应用程序在调用进程中运行组件,而服务器应用程序在 COM+ 创建的专用进程(称为 Dllhost.exe)中运行组件。COM+ 应用程序类型会影响托管组件的每个方面。服务器应用程序在单独的进程中运行,从而影响需要跨进程边界调用组件服务的应用程序的整体性能。进程间通信是库应用程序和服务器应用程序之间在性能和限制方面的差异的主要因素。
COM+ 基于上下文。上下文是具有相同执行要求的对象的环境。上下文可以拦截方法调用或对象激活,并作为结果执行 COM+ 服务之一。为了将 .Net 类注册到 COM+ 中,该类必须继承自 `ServicedComponent`。`ServicedComponent` 的实现基于 Remoting,Remoting 可以轻松地用于拦截方法调用。当 CLR 需要实例化新的派生的 `ServicedComponent` 对象时,会调用托管 C++。托管 C++ 调用 `CoCreateInstance`,该函数允许 COM+ 将对象绑定到非托管上下文,并提供为 COM+ 目录中注册的类预定义的非托管服务。CLR 还可以利用程序员与类相关的属性来设置 COM+ 中的类元数据(如果尚未设置)。在注册期间,通常指向 COM DLL 或 COM EXE 路径的 InProcServer32 键会指向 .NET JIT 引擎 mscoree.dll(讽刺的是 mscoree.dll 是 COM DLL)。由于回调到 CLR,实际创建的对象是真正的托管对象。
描述的过程发生在 COM+ 库对象需要时,最终是一个由 CLR 创建的纯托管类。注册为服务器应用程序的对象具有不同的实例化路径。服务器应用程序的跨进程特性要求使用 Remoting(以及 DCOM)从托管代码调用已注册的对象。实际上,在对象激活期间有两个 Remoting 调用:一个用于获取服务器对象 URI,另一个用于实际激活。COM 互操作调用在激活和数据反序列化期间发生。在所有情况下(库、服务器),对象都是由 CLR 创建的托管对象,只是服务器对象需要 Remoting 和 DCOM 来激活对象,并在每次调用时反序列化参数。我们将在性能部分看到这些差异的影响。
(i) 性能
在了解了与库应用程序和服务器应用程序激活相关的步骤后,让我们看看这些选项如何影响我们的 Web 应用程序性能。如前所述,本文侧重于实际方法,因此在检查 COM+ 应用程序性能时,我们将检查其他可能在特定情况下影响 Web 应用程序性能的 COM+ 服务。这些服务包括 JIT、对象池和同步支持。我无意深入探讨这些服务的细节,您可以在 http://www.ondotnet.com/pub/a/dotnet/excerpt/com_dotnet_ch10/index.html?page=1 或 MSDN 文档中找到它们的完整介绍。
性能测试基于采用保守层体系结构的常规 ASP.NET 应用程序。ASP.NET 应用程序由 6 个页面构成。
- GetDataNoCom.aspx:一个页面,从数据库获取约 20 条记录并显示。
- GetDaataCom.apsx:一个页面,调用 COM+ 中注册的类来检索约 20 条数据库记录并显示。
- GetLargeDataNoCom.aspx:一个页面,从数据库获取约 1000 条记录并显示其中一部分
- GetLargeDataCom.aspx:一个页面,调用 COM+ 中注册的类来检索约 1000 条数据库记录并显示其中一部分。
- LongInit.aspx:一个调用初始化时间较长的资源页面。
- LongInitCom:一个调用 COM+ 中注册的类页面。COM+ 类使用初始化时间较长的资源。
COM+ 对象将在作为库和服务器运行,并具有 JIT、对象池和方法调用时间时进行测试。测试在一台具有单个 Intel 4 1.80GHz CPU 和 500MB RAM 的机器上进行。数字结果是 ACT(应用程序中心测试)返回的 RPS(每秒请求数)。每个测试运行 5 秒,有 10 个并发用户。
表 1.0 测试结果 | |||||||||
无 COM+ |
库 |
服务器 | |||||||
池化 |
JITA |
调用时间 |
PRS |
池化 |
JITA |
调用时间 |
RPS | ||
GetDataNoCom – 90 条记录 |
300 |
X |
X |
X |
X |
X |
X |
X |
X |
GetDaataCom – 90 条记录 |
X |
- |
- |
- |
233 |
- |
- |
- |
111 |
X |
+ |
+ |
- |
200 |
+ |
+ |
- |
105 | |
X |
+ |
+ |
+ |
200 |
+ |
+ |
+ |
105 | |
GetLargeDataNoCom - 900 条记录 |
50 |
X |
X |
X |
X |
X |
X |
X |
X |
GetLargeDataCom – 900 条记录 |
X |
- |
- |
- |
49 |
- |
- |
- |
36 |
X |
+ |
+ |
- |
48 |
+ |
+ |
- |
28 | |
X |
+ |
+ |
+ |
48 |
+ |
+ |
+ |
28 | |
LongInit |
1 |
X |
X |
X |
X |
X |
X |
X |
X |
LongInitCom |
X |
- |
- |
- |
1 |
- |
- |
- |
1 |
X |
+ |
+ |
- |
0.3 |
+ |
+ |
- |
100 | |
X |
+ |
+ |
+ |
0.3 |
+ |
+ |
+ |
100 | |
正如您在表 1.0 中看到的,非 COM+ 页面和使用 COM+ 库的页面在性能上存在差异。当页面执行更多耗时任务时,性能差异会减小。相反,我们可以看到使用 COM+ 服务器应用程序的页面比使用 COM+ 库应用程序的页面慢 2 倍。一个例外是处理需要长时间初始化的任务,您可以看到使用池化可以极大地提高性能。稍后我们将详细介绍这个问题。您还可以看到,使用调用时间数据使我们能够监视应用程序组件,这对页面输出没有任何影响。稍后我们将看到如何使用这些数据来检查运行时应用程序问题。
使用 ACT 可以在开发过程中为您提供帮助。在开发过程中检查页面的 RPS(每秒请求数)可以帮助您在您或其他程序员编写代码时发现问题和瓶颈。在开发过程早期发现问题通常比在开发完成后解决问题更容易。我强烈建议尽可能使用 ACT 来在每个开发阶段充分掌握应用程序的状态。
(ii) 访问和限制
除了性能方面,如果我们选择使用 COM+ 应用程序,我们还需要考虑需要注意的限制。限制实际上仅与 COM+ 服务器应用程序相关,这主要是由于其进程外性质。服务器库类无法访问 ASP.NET 内部对象。实际上,如果页面多线程状态更改为单线程(使用 ASPCOMPAT 属性),则可以完成,但这会损害应用程序性能。
将 .Net 程序集托管在 DLLHOST.exe 中(这是一个运行在系统目录中的进程),会导致限制,因为 ASP.NET 应用程序和调用的 COM+ 服务器程序集运行在不同的位置,强制程序员将包含公共数据(接口、枚举等)的程序集托管在 GAC 中。将公共数据程序集放在 GAC 中会阻止将这些程序集部署到多个物理位置。
从 ASP.NET 应用程序调用 COM+ 服务器应用程序使用 Remoting 会带来其他一些限制。如果 ASP.NET 调用 COM+ 类并请求数据,一切都会顺利进行,因为 CLR 会处理已注册 COM+ 类的 Remoting。但如果 COM+ 类需要回调到正在调用的 ASP.NET 应用程序或返回引用类型(如 DataReader),则需要采取一些措施才能使页面类成为 Remoting 服务器。稍后我将展示如何实现这种行为。
Remoting 带来的另一个限制是需要传递支持序列化的参数。Remoting 会序列化对象以在 Remoting 通道上传输它们。将不支持序列化的对象作为参数传递会导致运行时错误。
由于每次调用注册为 COM+ 服务器应用程序的类都使用 DCOM 和 Remoting,因此最好使用“块式”调用而不是“琐碎式”调用。创建具有参数的少量调用,而不是为将从 ASP.NET 应用程序调用的类使用属性。“块式”调用优于“琐碎式”调用仅适用于“入口点”类。作为请求过程一部分使用的其他类应使用经典的 OO 方法。
(b) 使您的应用程序尽可能可用
.Net 旨在实现的一个战略目标是进入企业软件市场。微软希望渗透到这个长期以来由 Oracle 和 SUN(Java 和 Java 应用程序服务器供应商)主导的市场。企业市场的需求与电子商务市场的需求不同。通常,企业软件用于处理大型不同的数据库,并拥有一套内网应用程序,这些应用程序从企业最终用户那里被视为一个大型软件。这些应用程序(通常有几十个)通常由同一个服务器托管,该服务器是负载均衡集群的一部分。由不同开发团队构建的应用程序集,它们之间有大量连接,最终将被视为一个应用程序并托管在同一台服务器上,这与构建单个客户端服务器应用程序或电子商务应用程序相比,需要一套不同的要求。微软开始发布越来越多的软件工具和开发工具来帮助企业构建他们的软件。其中一种工具是 COM+,COM+ 的一大贡献是为可用性和可伸缩性提供了强大的支持。
在同一台服务器上托管许多应用程序有很多企业优势。这种配置由于应用程序之间的连接性而提供了最佳性能,并节省了金钱(想象一下为每个应用程序购买和维护服务器的成本)。但有一个很大的缺点。如果小时报表系统出现问题并消耗 100% 的 CPU,则同一 IIS 下运行的所有其他应用程序也会受到影响。这种情况会损害应用程序甚至数据的可用性,必须加以阻止,或至少进行监视和停止。
从性能和限制来看,一个明显的结论是最好不要使用 COM+ 服务器应用程序。尽管该结论基于可靠的证据,但 COM+ 服务器应用程序可以轻松解决可用性问题,但会以性能为代价。在大多数情况下,性能成本对于需要服务于极少数(可能数万用户,分布在三台或更多服务器上)的企业软件来说是可以忽略的。
(i) 最大隔离
将类注册为 COM+ 服务器应用程序最终会创建一个名为 dllhost.exe 的专用进程,当创建类对象时,该进程将运行这些对象。这种模式将这些类对象与调用应用程序的地址空间隔离,并允许我们在不终止调用 Web 应用程序的情况下终止类对象。
事实上,IIS5 和 IIS6 提供了其他隔离机制。IIS 5.0 捕获对 .Net 应用程序的请求,并通过管道将请求传输到一个专用的进程来处理 ASP.NET 应用程序 - aspnet_wp.exe。服务器上只有一个 aspnet_wp 进程,每个 Web 应用程序在 aspnet_wp 中都有自己的应用程序域。ASP.NET 利用了这种架构,通过重新启动 Web 应用程序的应用程序域来重新启动应用程序。虽然 ASP.NET 在某些情况下可以重新启动应用程序,但管理员却不行。作为管理员,您可以做的是使用 ASP.NET 机制,在应用程序 bin 目录发生任何更改时重新启动 Web 应用程序。这种变通方法仅在您的应用程序未处理传入请求时才有效。在这种情况下,应用程序重新启动将在请求完成后发生。如果其中一个请求处理步骤进入无限循环,Web 应用程序将不会重新启动。
这种 IIS5 的行为以及 Web 应用程序使用的程序集的进程内激活模式,可能导致一个应用程序出现故障时,不止一个应用程序不可用。为了克服这种情况,管理员需要重新启动 aspnet_wp 进程,从而重新启动服务器上运行的所有应用程序。防止这种情况的唯一方法是使用层分离,并将非 GUI 层作为 COM+ 应用程序托管,以便管理员可以使用 COM+ MMC 重新启动专用进程,而不会损害 aspnet_wp 进程。
IIS6 引入了应用程序池,可以托管一个或多个 Web 应用程序。应用程序池有自己的 w3_wp 进程,可以由管理员回收,从而允许终止一个 Web 应用程序而不影响其他应用程序。IIS6 的这个选项允许管理员终止一个应用程序,但仍然缺少 COM+ 中内置的监视功能,这些功能使管理员能够快速找到出现故障的程序集。
现在让我们实际看看隔离。我们将构建一个简单的 Web 应用程序(AvailabilityCheck),其中包含一个注册在 COM+ 中的类的程序集。该类包含两个方法 DoItRight 和 NeverEndingStory,其中 NeverEndingStory 包含一个无限循环。Web 窗体包含两个按钮,每个按钮调用类的方法。
图 1.0
//Class code
namespace AvailabilityCheckDll
{
public class AC : System.EnterpriseServices.ServicedComponent
{
public AC()
{
}
public string DoItRight()
{
return DateTime.Now.ToString ();
}
public string NeverEndingStory()
{
// simulate unfinish recursive
for(;;)
{
}
return DateTime.Now.ToString ();
}
}
}
// Page Code
public class WebForm1 : System.Web.UI.Page
{
protected System.Web.UI.WebControls.Button btnDoItRight;
protected System.Web.UI.WebControls.Button btnNeverEndingStory;
private void Page_Load(object sender, System.EventArgs e)
{
}
+#region Web Form Designer generated code
private void btnDoItRight_Click(object sender, System.EventArgs e)
{
AvailabilityCheckDll.AC oAc = new AvailabilityCheckDll.AC();
Response.Write ( oAc.DoItRight ());
}
private void btnNeverEndingStory_Click(object sender, System.EventArgs e)
{
AvailabilityCheckDll.AC oAc = new AvailabilityCheckDll.AC();
Response.Write ( oAc.NeverEndingStory());
}
}
运行 Web 应用程序会导致 AC 类注册为库应用程序。单击 DoItRight 按钮会返回一个显示日期和时间的页面。单击 NeverEndingStory 会导致 CPU 占用率达到 100%。唯一可以结束 100% CPU 利用率的方法是重新启动 aspnet_wp(IIS5)或回收应用程序池(IIS6)。现在,在 COM+ MMC 中更改 AC 的元数据,使其作为服务器应用程序运行,然后按 NeverEndingStory 按钮。在注意到 CPU 占用率达到 100% 后,只需关闭 COM+ 应用程序。请注意,CPU 使用率会恢复正常,并且您会收到一个错误页面,指示您的应用程序存在问题。此场景展示了使用 COM+ 服务器应用程序的优势。您已经看到了如何轻松地停止恶意代码,而不会损害其他 Web 应用程序。
另一种获得隔离的方法是使用 Remoting 并将程序集托管在专用的 Windows 服务中。管理员可以关闭专用服务而不会损害其他应用程序。这种方法有两个缺点。关闭服务会影响托管在该服务中的所有程序集,并且无法利用统计数据。
正如您所见,将类注册为 COM+ 服务器应用程序会提高应用程序的可用性,并结合监视功能可以极大地减少应用程序不可用时间。只是不要忘记将类注册为 COM+ 应用程序会损害您的应用程序性能。如我所述,在决定是否将程序集注册为服务器应用程序之前,最好使用 ACT(应用程序中心测试)检查 RPS(每秒请求数),然后再做决定。
(ii) 组件监视和快速问题解决
COM+ 服务器应用程序提供了一组数据和统计信息,可用于快速定位恶意代码。如果 Web 应用程序使用的所有程序集都已加载到应用程序域内存地址中,那么知道错误发生在哪里(如果您收到错误消息的话)的唯一方法是通过冒泡异常并将它们写入日志文件。即使程序员遵循了这种做法,服务器管理员也很可能无法理解这些日志。将类注册为 COM+ 服务器可以在多个方面帮助管理员。
- 进程信息。每个注册为服务器应用程序的类都有自己的专用进程。COM+ MMC 让您知道每个应用程序的进程 ID。您可以使用此数据在任务管理器中查找 CPU 使用率高的进程。将任务管理器中的 PID(可以从“查看”->“选择列”菜单添加 PID 列)与 COM+ 应用程序的状态视图进行比较,可以帮助您快速确定哪个 COM+ 应用程序导致 CPU 使用率过高。
- 组件信息。首先,要使用这些数据,必须在类声明中设置 `EventTrackingEnabled` 属性。设置此属性将生成组件状态视图中每个应用程序组件的统计信息。最重要的数据是 CallTime。此参数显示对象执行其任务所需的时间(毫秒)。使用这些数据,管理员可以找到瓶颈组件或导致应用程序出现故障的组件(具有巨大调用时间的组件)。此外,如果每个组件代表应用程序的一个层,管理员可以轻松地确定应用程序问题的根源是否深入到数据库层或逻辑层。
正如您所见,COM+ 导出的应用程序和组件数据可供管理员使用,以最大限度地缩短问题跟踪时间,从而最大限度地提高应用程序的总可用时间。与隔离一样,组件和应用程序数据仅在应用程序设置为服务器时收集(组件调用时间仅在应用程序设置为服务器时收集。其他统计数据为库和应用程序收集,但它们不像调用时间那样有用)。
您可以使用前面的代码示例来查看描述的数据如何实际应用,并帮助您找到有问题的进程和类。将 COM+ 应用程序设置为运行为服务器,并运行 Web 应用程序。按 NeverEndingStory 按钮。当 CPU 使用率达到 100% 时,打开任务管理器并切换到进程选项卡。按 CPU 使用率排序数据。在列表顶部,您应该看到 DllHost.exe。记下 DllHost.exe 的 PID,并打开 COM+ MMC 中 COM+ 应用程序文件夹的状态视图。查找具有相同 PID 的应用程序,您会发现 AvailabilityCheckDll 与相同的 PID 匹配。到目前为止,您已经知道哪个应用程序导致了问题,接下来是找到负责的类。打开应用程序组件状态视图。此应用程序只有一个类,但您可以看到一个组件的调用时间持续增长。该组件是导致 CPU 使用率过高的原因。
(iii) 应用程序回收(COM+ 1.5)
COM+ 1.5 版本进一步提高了 COM+ 应用程序的可用性和稳定性。COM+ 应用程序可能导致预期和意外的问题,例如内存泄漏或使用不可伸缩的资源。COM+ 允许管理员设置应用程序将在何种情况下自动回收。COM+ 允许您按进程生命周期、内存消耗、方法调用次数和对象激活次数设置回收。
(iv) 伸缩对象(COM+ 1.5)
COM+ 1.5 引入了一个新功能,可以伸缩旧的单线程应用程序(如 VB 6.0 构建的组件)。该功能允许管理员设置可以为给定应用程序启动的 DllHost 进程数量,并服务传入的请求。当应用程序池大小大于一时,COM+ 开始以轮循方式在 DllHost 进程之间路由请求。您可以在 COM+ MMC 的运行进程文件夹中看到所有进程,包括它们的 PID。除了提高可伸缩性外,应用程序池对于可用性和恢复也非常有用。如果由于某种原因一个 DllHost 发生错误,新的请求可以路由并使用池中的另一个 DllHost。
(v) 进程转储(COM+ 1.5)
完整的进程内存转储对于监视、分析和调试非常有帮助。转储对于查找和修复导致应用程序异常的问题至关重要。COM+ 1.5 通过添加一个选项卡来解决此问题,该选项卡允许管理员在发生异常时自动生成进程内存转储。COM+ MMC 允许您设置转储文件的位置以及在磁盘上回收之前将保留的转储文件数量。除了自动生成转储文件外,您还可以使用转储菜单在应用程序的上下文菜单中(在运行进程文件夹中的应用程序上)随时转储应用程序内存。创建转储文件后,您可以使用 WinDbg 打开转储文件并分析数据。
(vi) 允许不同版本的 COM+ 应用程序同时运行(COM+ 1.5 on .NET server)
此功能仅在 .Net 服务器上可用。您必须通过在 COM+ MMC 中计算机的“选项”选项卡中启用分区选项来启用 COM+ 分区。分区使管理员能够创建多个分区,每个分区托管具有不同元数据设置的服务器或库应用程序。此选项允许在同一台计算机上托管具有不同 COM+ 目录数据的多个 COM+ 应用程序版本。当 COM+ 应用程序组件激活需要时,COM+ 会根据激活组件的用户和附加到给定分区的用户来决定在哪个分区中激活对象。您还可以利用此功能通过设置不同的连接字符串作为 COM+ 构造函数字符串来分离不同开发人员组或用户之间的数据。
(vii) 展望美好未来
我希望到目前为止,您已经对 COM+ 服务器应用程序如何在创建更具可伸缩性和可用性的系统方面提供巨大帮助,以及相反,COM+ 服务器应用程序如何因激活和封送处理过程中涉及的 DCOM 而损害性能有了初步了解。我希望微软能够认识到这一点,并且 COM+ 的新版本将基于 .Net,从而淘汰 DCOM。在此之前,您需要自行决定是优先考虑性能还是稳定性与可用性。理论上,从服务器应用程序切换到库应用程序只需更改激活类型即可,但实际上,这两种激活类型之间存在服务器限制(我之前提到过)以及轻微的代码更改(您可以在测试应用程序中看到)。我的建议是,在创建应作为 COM+ 组件托管的类时,要考虑到这些限制和代码更改。这样做可以让管理员和项目经理决定您的程序集最终将如何运行。