.NET Remoting:设计决策和最佳实践






4.29/5 (18投票s)
2005年12月7日
14分钟阅读

61076
一篇关于如何根据您的需求进行 .NET Remoting 设计选择和开发规范的文章。
引言
良好的架构和设计是成功应用程序开发的基础。应用程序的成功在于其健壮性和可维护性。这些质量取决于您在项目开始时所做的设计和架构选择。使用正确的架构是确保应用程序性能和可扩展性的关键。然而,大多数时候,您必须在性能和可扩展性之间取得平衡,根据需求可能更偏向于其中一个。
在本文中,您将了解有助于您确定是权衡性能以换取可扩展性,还是反之亦然的因素。为了扩展分布式应用程序而产生的开销通常会导致性能下降。当您扩展现有应用程序时,您可以进行纵向扩展(scale up)或横向扩展(scale out)。纵向扩展,最常用于客户端/服务器场景,是指添加硬件,例如更多或更快的 RAM、磁盘和处理器;横向扩展是指添加更多服务器。您可以组合使用这两种机制。但是,如果您的应用程序没有设计为在多台计算机上运行,您就必须进行纵向扩展。因此,在设计分布式应用程序时,您面临的问题是是否启用可扩展性,还是专注于应用程序的性能。性能和可扩展性之间的选择不是简单的非此即彼:一个正确设计的、设计用于横向扩展的分布式应用程序,其性能可能优于传统的客户端/服务器应用程序。然而,通常来说,应用程序的可扩展性越强,性能损失越大。您部署的硬件对设计和您必须考虑的权衡有很大影响。
您的设计选择会影响应用程序的可扩展性和性能。为了做出正确的选择,您必须清楚地了解应用程序的需求以及 .NET Remoting 的特性。考虑到您的需求,您必须就以下因素做出决定:激活模型、通道和格式化程序、通用程序集的实现、同步或异步调用、事件以及默认对象生存期。
激活模型
有状态单例对象 |
有状态 CAO |
无状态单次调用 |
|
|
|
远程对象激活的方式对 Remoting 应用程序的关联性、状态管理、负载平衡、性能和可扩展性有很大影响。基于这些因素以及它们如何应用于您的架构,您可以实现服务器激活对象(SAO)或客户端激活对象(CAO)。
服务器激活对象
当有多个服务器为客户端提供服务时,您可以使用 SAO。使用 SAO 时,任何一个接收到请求的可用服务器都可以处理该请求。您可以使用单次调用 SAO 或单例 SAO。当您不需要维护对象状态时,请使用单次调用 SAO。另一方面,如果您需要进行状态管理,则使用单例 SAO。
客户端激活对象
当您使用单服务器方法时,CAO 工作良好。当您使用单服务器方法时,您不提供负载平衡或故障转移群集,并且您使用为特定客户端维护状态的组件。通过 CAO,您可以完全控制对象的创建和远程对象的生存期。
通道和格式化程序
选择合适的通道和格式化程序会影响消息传输的速度和互操作性。您希望部署 Remoting 应用程序的环境决定了应该使用的通道和格式化程序。
通道
在 .NET Remoting 中,有两种通道类型可供选择:传输控制协议 (TCP) 和超文本传输协议 (HTTP)。
- TCP 通道(
TCPChannel
类)为 Remoting 解决方案提供最大性能。TCP 通道不提供内置的身份验证和授权检查。因此,如果您的 Remoting 解决方案部署在没有防火墙的可信环境中,请使用 TCP 通道。 - 当您需要互操作性或需要允许通过防火墙进行通信时,请使用 HTTP 通道(
HTTPChannel
类)。
Formatter
在 .NET Remoting 中,您可以选择使用 SOAP 格式化程序或二进制格式化程序。与二进制格式化程序一样,TCP 和 HTTP 通道都可以与 SOAP 一起使用。当您需要互操作性以及在跨平台应用程序中使用时,请选择 SOAP。SOAP 格式化程序由于基于 XML,因此性能不是最优的。由于 XML 的自描述标签和元标签,这会增加性能开销。
TCP 和 HTTP 通道都支持二进制格式化程序。当性能是首要考虑因素时,请选择二进制格式化程序。二进制格式化程序减小了传输数据的尺寸,因此速度更快。
HTTP 通道 | TCP 通道 |
用于在 Internet 上通信 | 用于在内网中通信 |
能够访问 HTTP 服务器现有的安全系统 | 必须基于 .NET Framework 基类开发安全系统 |
通信效率较低 | 通信效率较高 |
SOAP 格式化程序 | 二进制格式化程序 |
使用 SOAP 发送信息 | 使用二进制消息发送信息 |
是应用程序之间使用不兼容架构进行通信的理想选择 | 只能由 .NET Framework 应用程序读取 |
基于文本 | 二进制且紧凑 |
实现通用程序集
使用通用和可重用程序集可以加快应用程序开发过程,并在一定程度上允许重用成功的架构。您可以将通用程序集作为私有程序集本地部署,或将其嵌入全局程序集缓存 (GAC)。当您必须确保库的各种版本之间的兼容性时,请选择在 GAC 中实现通用程序集。如果服务器和客户端都使用通用程序集,那么您可以通过使用接口、抽象类、存根基类或 Soapsuds 生成的代理来实现它。
接口适用于隐藏实现细节,可以用来避免为客户端部署通用程序集。抽象类与接口有一些共同的特性。但是,抽象类不能实例化,尽管它们可以也应该包含实现代码。然而,接口和抽象类之间的区别在于,抽象类可以作为参数传递给不同应用程序域中的方法。存根基类与接口和抽象类类似,因为存根基类用作远程类型的框架。但是,您可以从存根基类创建类的实例。Soapsuds 生成的代理最常用于您想要封装在代理中的非 Microsoft Web 服务。顾名思义,Soapsuds 生成的代理只能与 SOAP 调用一起使用。
通用程序集的选项
- 接口:接口定义了一组属性、方法和事件。与类不同,接口不提供实现。您将接口定义为与其实现它的类不同的独立实体。当一个类实现一个接口时,它必须实现该接口中声明的所有方法。接口必须定义设置和获取数据的方法。但是,接口不会实现这些方法。您使用接口来定义任何类都可以实现的行为协议。使用接口来
- 捕获不相关类之间的相似性,而无需人为强制类关系。
- 声明一个或多个类预计要实现的方法。
- 揭示对象的编程接口,而无需揭示其类。
- 抽象基类:抽象类有一个或多个抽象方法。抽象类是通用概念的表达,您可以从中派生出更具体的类。抽象类充当其他类可以继承属性和方法的基类。您不能创建抽象类类型的对象;但是,您可以传递对抽象类类型的引用。您使用抽象基类来提供默认行为或定义系列。
- 存根基类:您不能直接实例化抽象类或接口。如果您想直接使用 `new` 运算符在客户端上创建远程对象,可以将远程类型在通用程序集中定义为非抽象类。存根类旨在作为您的远程类型的框架;使用存根类就像使用接口或抽象类一样。您仍然应该在服务器上将远程类型实现为派生自存根的类。由于存根不是抽象的,因此有可能意外地创建存根基类的本地实例而不是远程对象。
- SOAPSuds 生成的代理:SOAPSuds 是一个命令行工具,它生成一个包含表示远程对象的代理类的程序集。SOAPSuds 工具除了生成程序集外,还可以生成代码。前提是对象使用 HTTP 通道托管,您还可以使用 SOAPSuds 为托管在另一台计算机上的远程对象生成代理。要使用 SOAPSuds 生成的代理,您的通道必须使用 SOAP 格式化程序。
- 工厂设计模式:工厂模式的大多数实现都使用两个抽象类 `Factory` 和 `Product` 来提供不变的接口。虽然您可以使用纯接口来实现工厂模式,但使用抽象类可以允许您在抽象类中放置通用的实例行为。此外,抽象类比纯接口具有版本控制优势。
实现工厂设计模式时,请遵循以下指南
- 不要直接在服务器上创建 CAO 的实例。而是使用 SAO,它充当工厂并向客户端返回实际的 CAO 对象。
- 确保 SAO 工厂有一个或多个方法来创建 CAO 对象。
- 不要将 CAO 的实现暴露给客户端。创建一个接口,让客户端使用该接口对 CAO 对象进行调用。
- 不要将服务器代码(远程对象实现)暴露给客户端。分发接口。
同步与异步调用
您可以使用同步或异步调用来调用远程调用。选择最合适的远程调用方法可以提高应用程序的响应能力,并可能提高性能。
同步调用
当您不需要并行处理客户端请求时,请使用同步调用。使用同步调用的优点是您可以立即捕获返回值并抛出异常。使用同步调用的缺点是应用程序响应速度较慢,因为 Remoting 服务器必须等待所有客户端确认然后处理回调。
异步调用
如果您需要良好的客户端响应能力,请考虑异步调用远程方法。例如,在 Microsoft Windows Forms 应用程序中使用异步调用。但是,如果您的客户端是 ASP.NET 应用程序,异步调用方法然后阻塞调用线程以获取返回值可能会导致线程池耗尽。另外,如果服务器宕机,客户端会抛出异常。这是因为客户端不知道服务器的状态。唯一的真正解决方案是定期回收客户端应用程序。
是否使用事件
使用事件和回调可能会在服务器上产生开销,因为服务器会持有已注册发布服务兴趣的所有客户端的引用。无论您是使用同步还是异步调用来注册事件,在客户端或服务器端都可能出现问题。为了提高响应能力,您可以使用消息队列(也称为 MSMQ)。消息队列使服务器能够在不等待客户端确认的情况下将消息发送给客户端。
是否使用默认对象生存期
.NET Remoting 使用租约赞助商进程来管理对象的生存期。对象的默认生存期设置为五分钟。要确定适合您应用程序的对象生存期,您必须在服务器上的资源使用情况和频繁销毁和重新创建对象的性能影响之间取得平衡。以下是您在必须更改对象的默认生存期时可以使用的一般指南:
- 为创建成本较高的对象使用更长的租约时间。
- 如果您使用创建成本较高的对象,请增加默认租约超时时间,以便对象停留更长时间。
- 为消耗大量共享资源的对象使用更短的租约时间。
- 如果您创建使用共享资源的对象,请为该对象使用更短的租约时间。当您缩短默认生存期时,对象将被销毁,资源将被快速释放。
托管选项
将远程应用程序托管在 Microsoft Internet Information Services (IIS) 上比在 Windows 服务中托管应用程序需要更少量的开发工作。然而,IIS 的性能比 Windows 服务慢,因为在 IIS 中托管应用程序会增加 Web 服务器的开销。此外,IIS 只支持 HTTP 协议。由于这个限制,您无法利用 TCP 的性能,而 TCP 是一种开销小得多的协议。如果计算机上未出于安全原因安装 IIS Web 服务器,则 Windows 服务很有用。技术支持人员也更容易管理 Windows 服务并确保服务正常运行。最后,如果您不需要托管网页,使用 Windows 服务是更好的选择,因为您不必维护 Web 服务器,这需要特定的产品支持知识和额外的服务器资源。
质量 | 命令行窗口 | Windows Forms | Windows 服务 | IIS |
推荐用于原型开发 | 是 | 是,当使用配置文件时 | 否 | 否,需要虚拟目录设置 |
与 Windows 自动启动 | 否 | 否 | 是 | 是,如果 IIS 设置为自动启动。 |
推荐用于生产 | 否 | 否 | 是,当您希望获得持续的可用性时 | 是,当您希望快速生产时 |
容错 | 否 | 否 | 是,关闭后 | 否 |
内置安全选项 | 否 | 否 | none | SSL、证书、内置身份验证 |
Remoting 的最佳实践
在适当的场景中使用 Remoting
.NET Remoting 是进程内、跨应用程序域通信的首选通信机制。对于跨进程、跨服务器或跨部署或信任边界的通信,ASP.NET Web 服务通常是推荐的选择,因为它们基于行业标准,易于实现互操作性。但是,如果状态和原始性能(可以使用 TCP 通道和二进制格式化程序实现)很重要,并且互操作性不是问题,那么 Remoting 是更好的选择。
设计粗粒度接口
频繁调用的接口(chatty interfaces)会导致执行单个逻辑操作需要多次往返。为了减少往返次数,从而提高性能,请使用粗粒度接口(chunky interfaces)。在粗粒度接口中,您可以在一次往返中传输必要的数据。相反,频繁调用的接口通常通过属性来实现。这意味着您设置一系列属性,然后运行一个或多个方法。这对于面向对象编程 (OOP) 通常是合适的,但对于 Remoting 来说不是一个合适的选项。
使用高效的数据类型
在 Remoting 中使用最有效的数据类型非常重要。例如,如果您使用 `DataSet` 对象来存储数据,该对象可以包含多个表,并且可以通过一次方法调用将其序列化和反序列化为可扩展标记语言 (XML)。但是,您必须认识到使用 `DataSet` 对象可能会由于 XML 的序列化和反序列化而增加网络流量。
尽可能使用单次调用 SAO
单次调用对象在每次客户端请求时都有一个唯一的实例。因此,单次调用对象仅在方法调用期间存在。这些对象不维护状态信息,因为它们在处理完方法后会被销毁。由于单次调用对象不存储状态信息,因此它们不受同步代码的限制,从而消除了状态管理的开销。在 IIS 中使用 HTTP 通道托管的单次调用对象为您的 .NET Remoting 解决方案提供了最大的可扩展性。
避免阻塞操作
在 Remoting 中,阻塞意味着在某个进程运行时,其他进程必须等待该运行进程完成执行。因此,如果您的应用程序中有许多长时间运行的操作同时发生,那么许多线程将处于等待其他进程完成执行的状态。这种情况会导致吞吐量低和 CPU 利用率低。您必须识别任何可以同时运行的任务,并设计您的应用程序以支持这些任务。以这种方式设计您的应用程序可以提高 Remoting 服务器的请求/响应性能。
在可信服务器环境中,使用 TCP 通道和二进制格式化程序
在可信服务器环境中,您没有防火墙。因此,为了获得最佳性能,请使用带二进制格式化程序的 TCP 通道。此外,由于您处于可信环境中,因此无需实现额外的安全机制,例如数据加密。例如,在内网中,如果所有用户都通过域进行身份验证,您可以允许所有域用户访问 Remoting 应用程序。
控制对象生存期并避免内存状态管理
您必须控制对象生存期,以便能够有效地管理对象的创建并释放与对象关联的资源。此外,您必须避免在内存中存储状态。糟糕的内存状态管理会阻碍可扩展性和性能,并可能使故障转移支持变得不可能。