.NET Remoting Use Cases and Best Practices [翻译]

.NET Remoting Use Cases and Best Practices

 

Written by Ingo Rammer

Translated by Allen Lee

 

      大多数没有直接接触过我的人会假设我认为 .NET Remoting 比其它分布式应用程序的开发方法更有价值。他们也会假设我把 Remoting 当作任何分布式应用程序的万能解决方案。

      他们错了。事实上,我会为给定的任务选择合适的工具。除了 .NET Remoting ,你可以找到许多不同的技术开发分布式应用程序,而它们每个都有特定的用途:Enterprise Services 和 COM+ 、直接的 TCP/IP 套接字连接、UDP 数据报(UDP datagram)、MSMQ 消息、经由 http 的 Web Service 、经由可靠基础设施的 SOAP 消息、SQL XML 以及其它更多的。

      在本文里,我将会讨论 .NET Remoting 的各种使用环境、.NET Remoting 里一些你应该尽可能避免使用的特性,以及一些不会用到 .NET Remoting 的应用情景。讨论这些的最终目的是为了开发出稳定的、可靠的和可扩展的分布式应用程序。

      首先让我们看看可以应用 .NET Remoting 的几个不同环境。Remoting 可以用在你希望你的方法调用 …

  • ... 跨越 AppDomain 边界
  • ... 跨越本地机器上的进程边界
  • ... 跨越 LAN 上的机器边界
  • ... 跨越 WAN 上(仍然在同一个环境里)的机器边界

      接着,我们来看看 Remoting 提供的各种不同的特性:

  • SingleCall 服务器端激活的对象(SingleCall SAO)
  • Singleton 服务器端激活的对象(Singleton SAO)
  • 客户段激活的对象
  • 服务器端主办方(sponsor)
  • 客户端主办方
  • 事件和回调
  • TCP 信道
  • HTTP 信道
  • 自定义宿主(host)
  • IIS 宿主

      写这篇文章的主要原因是并不是所有特性都适用于所有应用程序环境的。在做深入解释之前,先来看一张特性/情景矩阵图以及使用对应的组合是否合适:

  SingleCall Singleton CAO Sponsors@Server Sponsors@Client Events TCP HTTP CustomHost IIS
AppDomain X X X X X X     X  
Process/Local X X X X X X X X X  
Process/LAN X + * *       X   X
Process/WAN X + * *       X   X

      X:完全没问题
      +:由于有可能保存跨方法的状态而对扩展性有所影响。如果小心地设计和开发,那么将不会出问题。
      *:对扩展性造成负面影响。如果你想扩展到多台服务器上,那么就不要使用了。
      空白:不推荐。

      但是,你不可能就拿这张矩阵图而不需要任何进一步的解释,对吧?那么,让我谈谈这些不同的情景以及我是基于什么理由来提出这个结论的。

 

跨越 AppDomain 的 Remoting

      你一在 .NET 里创建一个新的应用程序域,这两个 AppDomain 就会在后台使用 Remoting 相互通讯。这样的话,Remoting 会为你设置所有的信道和接收器(sink),实际上,它会选用一个最优的格式化处理程序(formatting process)和一个内存(in-memory)信道。

      这里蕴含这两层不同的含义: 1 、你不能更换格式化程序或者信道接收器链; 2 、你无需太过在意它。它就是能工作的。你也可以使用 .NET Remoting 的所有特性而不产生任何问题。事实上,跨越 AppDomain 的调用是其中一个主要的 .NET Remoting 用例。由于它们很好地集成到框架里,因此你通常不会发觉你在使用 Remoting 。

安全的特性

  • 所有

 

在同一台机器上跨越进程

      假设你有两个运行在同一台机器上的 Windows Forms 应用程序,你想让它们能够相互通讯。或者假设你有一个和你的 GUI 应用程序交换数据的 Windows 服务。 你可以使用哪种协议? Remoting !

      这是 TcpChannel 派上用场的其中一个事例,因为它允许你在构建它的时候通过指定 rejectRemoteRequests = “true” 来限制所有进来的连接必须是来自你自己的机器的。在这种情况下不必过多考虑安全问题。(然而,如果你在 WinForm 之间的通讯使用固定端口号,那么当你的应用程序运行在 Windows Terminal Services 上并且在同一时间里有两个或者更多的用户试图使用时将会出问题。)

      我还要告诉你一个关于 .NET Remoting 的特性的好消息,所有特性在同一台机器上的客户端和服务器端上都能正常工作。

安全的特性

  • 所有

 

在 LAN 里的多台机器上跨越进程

      好吧,现在我们来看一下现实中的情况。这是你通常的分布式 LAN 应用程序。这种应用程序还可以分为两个不同的类别:

  • 单一服务器(single-server)应用程序
  • 可扩展应用程序

不要使用事件和回调

      无论你的应用程序属于哪个类别,我强烈建议不要再网络应用程序里使用事件、回调或者客户端主办方。是的,使用它们是可以的。是的,在应用某个工作区(workaround)后它们可能会工作。真正的问题在于它们不太稳定,而且性能也不如预期那样好。导致这个稳定性 / 性能缺点的是调用模型。首先,你必须从服务器的角度考虑以同步还是异步的方式调用事件。对于第一种情况,服务器必须等待所有客户端回应和处理回调,这极大地增加了请求时间。然而,如果你决定以异步方式使用它们,你可能会遇到许多不同的问题,轻则线程池出现线程短缺,重则事件丢失和应用程序锁定。

      但是,如果你希望收到客户端的通知呢?在这种情况下,我会根据可靠性需要考虑 UDP 或者消息队列。使用 MSMQ 允许服务器端应用程序向监听的客户端发送消息而无需等候接收回应(或者等候客户端完成处理)。 这样就可以更好地处理请求的周转了。

你认为客户端激活对象怎么样?

      那么为什么我也把客户端激活的对象包含到在这个环境里不推荐使用的特性列表里呢?理由是 CAO 总是和激活它们的机器绑定在一起的。这意味着你不能为这些对象使用负载平衡(load balancing)或者故障转移群集(failover clustering)。另一方面,如果你使用 SingleCall SAO,你就可以很容易地使用 Windows Network Load Balancing (NLB)把方法的调用随机地分派给多个可用服务器中的一个。

      如果你运行的是一个单一服务器应用程序,那么这对你来说没什么所谓。但是,如果应用程序有可能用到服务器端群集上,那么 CAO 将毫无疑问限制了它的可扩展性。

      但是,你不应该仅仅考虑可扩展性:CAO 同样会对单一服务器的情况造成影响。当运行 SingleCall SAO (以及在数据库里精确地保存所有状态信息)时,你可以在需要时关闭和重启你的服务器。例如,你可以升级到新的版本或者应用一些补丁(bug fix)而用户无需关闭并重启你的客户端应用程序。然而,你一使用 CAO 就会马上失去这个特性。如果你重启了承载 CAO 的服务器,客户端应用程序将会在调用这些对象的任何方法时接收到异常。 CAO 不会在你重启服务器之后恢复。如果你想获得透明的可重启性(restartability)和最大的可扩展性,请不要使用它们。

最佳的信道 / 格式化程序搭配是哪组?

      对于任何跨越多个宿主的应用程序,我推荐使用 HttpChannel 和二进制格式化程序。理由很简单:你可以在标准的 Visual Studio .NET 项目里开发和调试你的应用程序,而在部署时你可以轻易在 IIS 上承载服务器端组件,这样做有许多好处:内建的验证机制(基本 HTTP 验证或者 Windows 集成验证)、内建的加密机制(SSL)以及可以关闭 HTTP keep-alive 功能,由于这样能减少对群集中个别服务器的依赖,因此可以进一步提高应用程序的可扩展性。

安全的特性

  • 承载在 IIS 上并使用 HttpChannelBinaryFormatter 的 SingleCall SAO
  • 就这样。如果你想安全的话,那么除此之外不要使用其他特性了。另外还要记住,当你在服务器端的方法返回一个 MarshalByRefObject 时,你实际上创建了一个类似于 CAO 的对象,因此应该避免,如果你希望项目具有可重启性和最大的可扩展性的话

 

经由 WAN/Internet 跨越进程

      随着应用程序的发展,你冲出了局域网的边界,另一些问题也随之而来。处于第一位的绝对是网络延迟。你不得不通过粗粒度接口(chunky interface)减少跨网络调用的次数。在粗粒度接口里,你会在网络上的一次往返中传输尽可能多的(并且尽可能都是必须的)数据。例如,如果客户端应用程序处理客户对象和地址,那么当客户端应用程序请求某个客户的信息时,你一定会传递这个用户的所有已知地址。一个可选的做法是使用两个不同的方法 GetCustomer()GetAddresses() ,但这样会使网络的周转次数加倍,因而极大地减少了应用程序的响应次数。

      然而,你应该注意在这里取得一个平衡。同时传输客户的全部订单信息或者完整的联系历史可能不是最佳的做法,如果在 99% 的情况下你不需要那些数据的话。这确实是一个平衡的问题。

      我想,对于这样的应用程序,我可以提供的最重要的建议是在低带宽、高延迟的网络环境中进行开发。不要在通过 LAN 连接的多台机器上运行服务器端和客户端。相反,使用简单的旧式调制解调器把客户端连接到 Internet 。这样做可以让你在开发期间发现并体验存在性能问题的地方——没有比用户响你的电话并且告诉你应用程序慢得很更令人感到难为情了。

      关于 Remoting 的特性的使用,我能给你的建议基本上和 LAN 环境下的一样:使用承载在 IIS 上并使用 HttpChannelBinaryFormatter 的 SingleCall SAO。另外,你还应该保证绝对不会使用事件、回调或者客户端主办方,因为这些特性在客户端的计算机和服务器之间存在着防火墙、代理服务器(proxy)或者 NAT 设备时可能无法工作。然而,在 LAN 环境中使用事件可能仅仅导致应用程序不稳定,它们也将妨碍应用程序在 WAN 环境下工作。

安全的特性

  • 承载在 IIS 上并使用 HttpChannelBinaryFormatter 的 SingleCall SAO

 

不使用 Remoting 的情景

      在介绍了 .NET Remoting 的四种不同的应用情景之后,我想说一下在哪些情况下我将不会使用 Remoting。

让我们使用 SOAP 吧!

      如果你打算使用 SOAP Web Service 整合不同的平台或者不同的公司,我强烈建议你考虑 ASMX (ASP.NET) Web Service 而不是 Remoting 。这些 Web Service 是基于工业标准的,与 Web Services Enhancements (WSE)等框架结合使用,就能以平台独立和面向消息的方式使用所谓的 GXA 或者 WSA 规范(WS-Security 、WS-Routing 、WS-Policy 、WS-Trust 、WS-SecureConversation 等等)的实现了。

      ASMX Web Service 提供了基本的 Web Service 特性,例如 WSDL-first 的开发、doc/literal 的使用、更为简单的 SOAP 标头(SOAP Header)检查等等。

      那么,让我再重复一次:如果你想要 SOAP ,结合 WSE 使用 ASP.NET Web Service 是你唯一的选择!

面向服务的体系结构

      当今工业界的一个流行术语是“面向服务的架构体系”(SOA),它在企业环境中提供平台独立、面向消息和松散耦合的服务。你可能已经猜到了: Remoting 并不是实现这些的正确选择。这里也可以考虑使用 ASMX+WSE 。

分布式事务、细粒度安全需求 ……

      一个完全不同的不适宜使用 Remoting 的情景是当分布式事务处理(distributed transaction)、细粒度安全需求(fine grained security requirement)、可配置进程隔离(configurable process-isolation)、发布和订阅事件等是必须的时候。是的,事实上你可以开发你自己的信道接收器(channel sink)并把它们插入 Remoting 框架以便能够使用这些特性。但是,你为什么要这样做?为什么要浪费时间?在 .NET 中已经存在另一个包含所有这些特性的框架了:Enterprise Services。

      如果你的应用程序可能用到任何下面所提到的服务,那么你就应该考虑使用 Enterprise Services 而不是 .NET Remoting 了:

  • 非常灵活的、可配置的验证和授权方式
  • 基于角色的安全机制,它使用与 Windows 用户帐户相独立的角色
  • 对象的实时(just in time)激活
  • 对象池(object pooling)
  • 进程隔离
  • 用作 Windows 服务的服务器端组件
  • 通过 MSMQ 让组件的交互自动排队

      另外, Windows Server 2003 上的 COM+1.5 还提供了一种叫做“没有任何组件的服务”(SWC)的特性。这使得你不必让组件继承自 System.EnterpriseServices.ServicedComponent 并在 COM+ 目录(COM+ catalog)中注册就可以使用 Enterprise Services 框架的大多数服务了。

 

Remoting 的九个准则

      Remoting 提供许多特性,它们的适用性会随使用情景的不同而不同。为了使你的应用程序能有最大的稳定性和扩展性,从而保证它的可靠性,如果你要让不同的机器相互通讯的话,那么你就应该遵循下面九个准则:

  • 仅使用以 SingleCall 模式配置的服务器端激活的对象
  • 使用 HttpChannelBinaryFormatter。如果你需要可扩展性、验证和授权等特性的话,把你的组件承载在 IIS 上。
  • 屏蔽 IIS 的 HTTP keep-alive 功能以便获得最大的扩展性。
  • 如果你想获得扩展性的话,请在开发期间在服务器群集中使用 Windows Network Load Balancing 。保证不与任何客户端相关联,并且保证在开发期间屏蔽 HTTP keep-alive 功能!
  • 不要使用客户端激活的对象,也不要跨越远程边界传递任何 MarshalByRefObject,如果你打算运行在群集上的话。你可以轻易地捕获这种情况,如果你使用 .NET Framework 1.1 版的话,因为它会在这种情况中抛出 SecurityException 或者 SerializationException。(是的,你可以修改这个设置,但你不应该这样做!)
  • 不要使用事件、回调合客户端主办方。
  • 不要使用静态字段为会被用户修改的操作数据保存状态信息。相反,总是把这类状态信息保存在数据库里。如果你在内存中保存不稳定的状态信息,那么当你试图把应用程序扩展到服务器群集上时将会出现问题。仅当信息不会改变时才缓冲它(像一组州名或者城市名),否则你将会潜入在群集上同步缓冲的恶梦。
  • 不要在从 .NET 到 .NET 以外的任何通讯上使用 Remoting 。在任何与 SOAP 、面向服务的架构体系和平台独立相关的情况中使用 ASP.NET Web Service 和 Web Services Enhancements(WSE)。
  • 不要试图使用自定义信道接收器来实现分布式事务处理和安全等特性。相反,使用 Enterprise Services ,如果它适用于你的环境的话。 .NET Remoting 不是中间件(middleware),它仅仅是一个传输协议,如果你需要服务,那就使用面向服务框架。当然,你也可以使用 .NET Remoting 访问 Enterprise Services 的组件。

      如果你遵循这些指导原则,那么你的 Remoting 应用程序将会非常稳定、可靠和可扩展。

 

关于作者

      Ingo Rammer 主要在欧洲中部从事独立咨询、开发、写作和培训等工作。2002年出版了《Advanced .NET Remoting》《Advanced .NET Remoting in VB.NET》。在他的日常咨询工作中,他主要关注 .NET 应用程序的架构体系,并为电信业和软件业的多家公司服务。你可以通过 http://www.ingorammer.com 与他取得联系。

 

关于本文

      前段时间由于需要开始了 .NET Remoting 的研究之旅,中间有幸拜读 Ingo Rammer 先生的《Advanced .NET Remoting》,跟随着书中的连接机缘巧合之下找到了本文。拜读之后,觉得可以把本文的翻译作为一个阶段性的总结(也可以说是偷懒,哈哈),随即书信 Ingo Rammer 先生表明意思,在征得他的同意后便开始了本文的翻译之旅了。若想查看原本,请点击这里

posted @ 2006-12-14 22:51  Allen Lee  阅读(...)  评论(...编辑  收藏