第5章分布式系统模式 使用服务器激活对象经过 .NET Remoting 实现 Broker

正在使用 Microsoft? .NET Framework 构建一个须要使用分布式对象的应用程序。您的要求包括可以按值或按引用来传递对象,不管这些对象驻留在同一台计算机上,仍是驻留在同一个局域网 (LAN) 中的不一样计算机上,或者是驻留在广域网 (WAN) 中的不一样计算机上。应用程序不须要您显式控制远程对象的生存期。html

关于 .NET Remoting 的背景信息数据库

远 程处理使用对象引用来进行客户端和服务器之间的通讯。在服务器激活状况下,客户端使用远程处理基础结构 (Activator.GetObject) 来检索对现有服务器对象的引用。有了对象的引用后,就能够调用对象的方法,就好像该对象在您的进程中,而没有运行在不一样的计算机上。如下基础机制用于实现 该功能:安全

  • 客户端检索远程类型的实例。
  • 远程处理基础结构建立一个充当远程类型的代理对象。
  • 客户端调用该代理的方法。远程处理系统收到调用、将其路由到服务器进程、调用服务器对象,而后将结果返回给客户端代理,客户端代理再将结果传递给客户端对象。

调 用自己必须以某种方式在客户端和服务器之间进行发送。远程处理基础结构将该机制称为传输通道。通道在应用程序之间跨越远程处理边界传输消息,不管这种边界 是应用程序域之间、进程之间仍是计算机之间的边界。通道能够在端点上侦听入站消息;将出站消息发送到另外一个端点,或者同时执行这两个任务。这样,您就能够 插入各类协议,即便公共语言运行库不在通道的另外一端。服务器

虽然服务器进程知道关于每一个惟一对象的一切信息,但客户端只知道它须要引用另外一个应用程序域中的某个对象(可能在另外一个计算机上)。从服务器应用程序域以外的范围来看,该对象是经过 URL 定位的。网络

服务器激活框架

正如分布式系统群集的介绍中描述的那样,.NET Framework 支持两种激活模型:服务器激活和客户端激活。服务器激活对象是其生存期直接由服务器控制的对象。只有当客户端调用对象的方法时,而不是在客户端调用 newActivator.GetObject() 时,服务器应用程序域才会建立这些对象;这样能够减小只是为了建立实例而发生的网络往返通讯。当客户端请求一个服务器激活类型的实例时,只会在客户端应用 程序域中建立一个代理。不过,这还意味着服务器激活类型只容许有默认的构造函数。若是要发布的类型的实例将以须要接受参数的特定构造函数来建立,那么,可 以使用客户端激活。tcp

为了建立服务器激活类型的实例,客户端一般使用 Activator.GetObject().分布式

选择协议和序列化机制ide

您 选择的协议类型会影响应用程序执行的方式。有关为应用程序选择正确的通道类型的某些标准,请参阅《.NET Framework Developer's Guide》中的“Choosing Communication Options in .NET”主题,您能够访问 MSDN? 开发人员程序网站:http://msdn.microsoft.com/library/ 来了解有关内容。函数

在此模式中,您将看到 HttpChannel/SOAP 和 TcpChannel/Binary 这两个示例。

实现策略

该模式提供了服务器激活对象的两个示例,以及 .NET Remoting 基础结构的灵活性。第一个示例使用 HttpChannel 及其默认的序列化机制 SOAP。第二个示例使用 TcpChannel 及其默认的二进制序列化机制。在讨论应用程序自己以前,咱们首先要了解必须在整个网络中分布的类。

服务器对象

RecordingsManager 类有一个名为 GetRecordings 的方法,它从数据库中检索一列记录,而后在 DataSet 中返回结果。注意,在肯定要经过远程链接传输的最佳数据类型时,会涉及到一系列考虑因素。该示例使用 DataSet,由于它的示例代码很简短,而且显示了复杂数据类型是如何传送的。有关本主题的深刻探讨,请参阅 MSDN 文章“Designing Data Tier Components and Passing Data Through Tiers”:

http://msdn.microsoft.com/library/en-us/dnbda/html/BOAGag.asp

RecordingsManager.cs

如下示例显示了 RecordingsManager 类:

using System;

using System.Data;

using System.Data.SqlClient;

public class RecordingsManager

{

   public DataSet GetRecordings()

   {

      String selectCmd = "select * from Recording";

      SqlConnection myConnection = new SqlConnection(

         "server=(local);database=recordings;Trusted_Connection=yes");

      SqlDataAdapter myCommand = 

         new SqlDataAdapter(selectCmd, myConnection);

      DataSet ds = new DataSet();

      myCommand.Fill(ds, "Recording");

      return ds;

   }

}

必须以远程方式访问该类。首先,RecordingsManager 类必须从名为 MarshallByRefObject 的远程处理基础结构中的一个类继承而来。MarshalByRefObject 是那些经过使用代理交换消息来跨越应用程序域边界进行通讯的对象的基类。不是从 MarshalByRefObject 继承的对象会以隐式方式按值封送。当远程应用程序引用一个按值封送的对象时,将跨越远程处理边界传递该对象的副本。由于您但愿使用代理方法而不是副本方法进行通讯,所以须要继承 MarshallByRefObject。其次,您须要从该类提取一个接口。接口对于减小客户端和服务器之间的依赖性是必不可少的,另外,也能够更好地部署应用程序。有关详细信息,请参阅该模式后面的“部署考虑事项”。

IRecordingsManager.cs

如下是提取的 IRecordingsManager 接口的代码:

using System;

using System.Data;

public interface IRecordingsManager

{

   DataSet GetRecordings();

}

RecordingsManager.cs (启用远程支持)

更改 RecordingsManager 后获得如下代码:

public class RecordingsManager : MarshalByRefObject, IRecordingsManager

{ /*  */ }

HttpChannel:SOAP 序列化

选择此通道和序列化机制的主要动机是安全性和互操做性。经过以 Microsoft Internet 信息服务 (IIS) 为宿主的 HttpChannel,能够利用内置在 IIS 和 ASP.NET 中的安全功能。若是您选择任何其余通道,或选择 HttpChannel 不驻留在 IIS 中,则必须提供本身的安全功能。另外,为了实现不一样操做系统之间的互操做,必须使用 HttpChannel 和 SOAP 序列化。不过,因为使用了 XML 序列化以及在 IIS 和 ASP.NET 内使用 HTTP 协议而须要额外的系统开销,选择 HttpChannel 并不能得到最高性能。有关详细信息,请参阅此模式后面的“操做考虑事项”。

如下解决方案将 HttpChannel 以及 SOAP 序列化用于前面描述的 RecordingsManager 类(请参阅图 1)。

1 HttpChannel 实现

HttpServer.cs

HttpServer 是一个控制台应用程序,该程序建立了 HttpChannel 对象并分配端口 8100。而后,代码将名称“RecordingsManager.soap”与一个 RecordingsManager 实例相关联。

服务器激活对象有两种激活模式:SingletonSingleCall

Singleton 类型在任什么时候刻只能有一个实例。若是实例已存在,则全部客户端请求都由该实例来处理。若是不存在实例,服务器将建立一个实例,而且全部随后的客户端请求都会由该实例来处理。

SingleCall 类型对于每一个客户端请求始终有一个实例。下一个方法调用将由其余服务器实例来处理,即便前一个实例还没有被系统回收。

RecordingsManager 使用 Singleton 激活模式,所以只有一个 RecordingsManager 实例运行在服务器上。这个过程颇有效,由于对象只有一个方法来检索一组预约义数据。最后一行确保用户按下 Enter 键以后代码才退出。请注意,也许这不是确保程序不退出的最佳方式。若是程序退出,客户端将没法访问服务器对象。

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Http;

public class HttpServer

{

   static void Main(string[] args)

   {

      HttpChannel channel = new HttpChannel(8100);

      ChannelServices.RegisterChannel(channel);

      RemotingConfiguration.RegisterWellKnownServiceType(

         typeof(RecordingsManager),

         "RecordingsManager.soap",

         WellKnownObjectMode.Singleton);

      Console.ReadLine();

   }

}

HttpClient.cs

客户端程序调用远程处理框架函数 Activator.GetObject(),从而指定对象所在的 URL 以及应该返回的类型。在本示例中的状况下,IRecordingsManager 对象应该在 http://localhost:8100/RecordingsManager.soap。有了实例以后,能够调用实例的方法,就好像它在同一个应用程序域中。

using System;

using System.Data;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Http;

public class HttpClient

{

   [STAThread]

   static void Main(string[] args)

   {

      HttpChannel channel = new HttpChannel();

      ChannelServices.RegisterChannel(channel);

      IRecordingsManager mgr = (IRecordingsManager)

         Activator.GetObject(typeof(IRecordingsManager),

         "http://localhost:8100/RecordingsManager.soap");

      Console.WriteLine("Client.main(): Reference acquired");

      DataSet ds = mgr.GetRecordings();

      Console.WriteLine("Recordings Count: {0}",

         ds.Tables["recording"].Rows.Count);

   }

}

TcpChannel:二进制序列化

选择此通道和序列化机制的主要动机是性能。实际上,使用二进制序列化就能显著提升性能。(请参阅“操做考虑事项”。)若是没有任何安全问题(例如,您正在构建一个彻底运行在防火墙内的小应用程序),应该使用 TcpChannel 和二进制序列化,由于这样会得到最佳性能。

如下解决方案将 TcpChannel 以及二进制序列化用于前面描述的 RecordingsManager 类(请参阅图 2)。

2 TcpChannel/ 二进制序列化实现

TcpServer.cs

TcpServer 是一个控制台应用程序,它建立 TcpChannel 对象并分配端口 8100。而后,代码将名称“GetRecordingsManager”与一个 RecordingsManager 实例相关联。RecordingsManager 的激活模式是 Singleton,所以只会有一个 RecordingsManager 实例运行在服务器上。最后一行确保用户按下 Enter 键以后代码才退出。请注意,也许这不是确保程序不退出的最佳方式。若是程序退出,客户端将没法访问服务器对象。

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

public class TcpServer

{

   static void Main(string[] args)

   {

      TcpChannel channel = new TcpChannel(8100);

      ChannelServices.RegisterChannel(channel);

      RemotingConfiguration.RegisterWellKnownServiceType(

         typeof(RecordingsManager),

         "GetRecordingsManager",

         WellKnownObjectMode.Singleton);

      Console.ReadLine();

   }

}

TcpClient.cs

客户端程序经过调用远程处理框架方法 Activator.GetObject(),以便在服务器上检索 RecordingsManager 对象的代理。该方法指定对象所在的 URL 以及应该返回的类型。在本示例的状况下,IRecordingsManager 对象应该位于:http://localhost:8100/GetRecordingsManager。有了实例后,能够调用该实例的方法,就好像它在同一个应用程序域中。

using System;

using System.Data;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

class TcpClient

{

   [STAThread]

   static void Main(string[] args)

   {

      TcpChannel channel = new TcpChannel();

      ChannelServices.RegisterChannel(channel);

      IRecordingsManager mgr = (IRecordingsManager)

         Activator.GetObject(typeof(IRecordingsManager),

         "tcp://localhost:8100/GetRecordingsManager");

      Console.WriteLine("Client.main(): Reference acquired");

      DataSet ds = mgr.GetRecordings();

      Console.WriteLine("Recordings Count: {0}",

         ds.Tables["recording"].Rows.Count);

   }

}

部署考虑事项

使用 .NET Remoting 时,必须在将应用程序部署到不一样的程序集中时多加当心。主要目标是要确保服务器上的代码不会传送到客户端。图 3 是 HttpChannel/SOAP 示例的 UML 部署图。

3 HttpChannel/SOAP 示例的结构

该示例使用一个名为 IrecordingsManager 的程序集,该程序集由客户端和服务器共享。该程序集包含 IRecordingsManager 接口,它定义了客户端和服务器正在共享的远程对象的接口。在该示例中,IRecordingsManager 程序集被下载到客户端。

测试

用 Nunit 为服务器编写测试相对比较简单。能够从服务器检索对象,而后将这些对象看成本地对象调用它们的方法。下面的类测试 HttpServer 类:

HttpServerFixture.cs

using System;

using System.Data;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Http;

using NUnit.Framework;

[TestFixture]

public class HttpServerFixture

{

   private IRecordingsManager mgr;

   private HttpChannel channel;

   private DataSet dataSet;

   [SetUp]

   public void LoadDataSet()

   {

      channel = new HttpChannel();

      ChannelServices.RegisterChannel(channel);

      mgr = (IRecordingsManager)

         Activator.GetObject(typeof(IRecordingsManager),

         "http://localhost:8100/RecordingsManager.soap");

      dataSet = mgr.GetRecordings();

   }

   [Test]

   public void RetrieveDataSet()

   {

      DataTable recording = dataSet.Tables["recording"];

      Assertion.AssertEquals(4,recording.Rows.Count);

      DataRow row = recording.Rows[0];

      string title = (string)row["title"];

      Assertion.AssertEquals("Up", title.Trim());

   }

   [TearDown]

   public void Release()

   {

      ChannelServices.UnregisterChannel(channel);

   }

}

结果上下文

使用服务器激活对象经过 .NET Remoting 来实现 Broker 具备下列优缺点。

优势

.NET Remoting 为全功能的分布式对象模型提供了运行在客户端和服务器上的全公共语言运行库语义。客户端和服务器之间所传递的数据的保真度不会受任何影响。该示例显示了如 何在客户端和服务器之间传递复杂类型 System.Data.DataSet。若是链接的两端没有公共语言运行库,就不可能实现这样的传递。

缺点

有些 Broker 优势会受到如下潜在缺点的影响:

  • 远程对象。 您必须记住,这些对象是远程对象。即便它们看上去像是本地对象,但从服务器来回封送数据仍然须要开销。记住,远程调用比公共语言运行库中的本地调用至少慢 1000 倍。所以,您应当只在须要时才进行这样的调用。因为须要最大限度地减小往返操做,这可能致使您在处理接口时不会使用最细的粒度。
  • 部署的复杂性。使用示例中描述的服务器激活对象时,在客户端请求对象以前,必须已经注册了该对象。这会使部署变得更加复杂。
  • 有限的互操做性。 您可使用 .NET Remoting 来构建 Web Service。不过,必须将端点限制为最简单的数据类型。例如,若是但愿可以与其余 Web Service 工具包进行互操做,必须将参数限制为内置的简单类型和您本身的数据类型(不要使用 .NET Framework 类型,例如 DataSet),而且使用服务器激活对象。
  • 更加复杂。与 Web Service 相比,.NET Remoting 更难学习、实现和调试。

安全考虑事项

要 使用 Microsoft Internet 信息服务 (IIS) 所提供的安全功能(例如,标准 HTTP 身份验证方案,包括基本验证、摘要式验证、数字证书,甚至 Microsoft .NET Passport),您必须使用一个基于 HTTP 的应用程序,并且该应用程序应当驻留在具备 ASP.NET 环境的 IIS 中。若是要使用其余任何传输协议,或使用 IIS 以外的 HttpChannel,都须要您提供安全机制。

操做考虑事项

以 下是 MSDN文章“Performance Comparison: .NET Remoting vs. ASP.NET Web Services”(.NET Remoting 与. ASP.NET Web Service 的性能比较)[Dhawan02] 中的性能比较的概述。该文的结论是,经过使用 TCP 通道和二进制序列化以及 Windows 服务主机,您能够实现最高性能。这种配置经过原始 TCP 套接字传输二进制数据,这比 HTTP 更有效。与 HttpChannel(它使用驻留在具备 ASP.NET 的 IIS 中的 SOAP 序列化)这种最慢的方法相比,其性能快 60%。

驻留在 IIS 中会致使性能降低,由于它涉及从 IIS (Inetinfo.exe) 到 Aspnet_wp.exe 的额外进程跳跃。不过,若是选择在没有 IIS 和 ASP.NET 的状况下驻留您的通道,则须要提供您本身的身份验证、受权和隐私机制。

相关文章
相关标签/搜索