第5章分佈式系統模式 使用客戶端激活對象通過 .NET Remoting 實現 Broker

正在 .NET 中構建一個需要使用分佈式對象的應用程序,並且分佈式對象的生存期由客戶端控制。您的要求包括能夠按值或按引用來傳遞對象,無論這些對象駐留在同一臺計算 機上,還是駐留在同一個局域網 (LAN) 中的不同計算機上,或者是駐留在廣域網 (WAN) 中的不同計算機上。

實現策略

這 種模式爲在 .NET Remoting 中實現客戶端激活對象提供了兩種實現方式。客戶端激活對象 (CAO) 和服務器激活對象 (SAO) 之間的主要區別在於,是什麼控制着遠程對象的生存期。在使用 CAO 的情況下,客戶端控制着生存期;而在使用 SAO 的情況下,服務器控制着生存期。這裏採用的示例在功能上類似於使用服務器激活對象在 .NET 中實現 Broker 中採用的示例。正如 .NET 文檔和示例中描述的那樣,第一種實現使用了客戶端激活。這種實現展示了客戶端激活對象的能力;不過,它們也有一些缺點。第二種實現(稱爲混合法)則解決了這些問題。

客戶端激活對象實現

RecordingsManager 類有一個名爲 GetRecordings 的方法,它從數據庫中檢索一列記錄,然後在 DataSet 中返回結果。該類擴展了 MarshalByRefObject 類,以確保在遠程處理情況下使用 Broker 對象,而不是將對象從服務器複製到客戶端。這裏描述的功能與"使用服務器激活對象在 .NET 中實現 Broker"中所描述的示例的功能完全相同。

RecordingsManager.cs

以下示例顯示了 RecordingsManager類,該類負責從數據庫中檢索 DataSet:

using System;

using System.Data;

using System.Data.SqlClient;

public class RecordingsManager : MarshalByRefObject

{

   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;

   }

}

HttpServer.cs

以下代碼將服務器配置爲允許使用 new 運算符創建客戶端激活對象。該代碼利用應用程序名以及要創建的對象的類型來配置服務器,而不是實際地註冊一個示例(如 SAO 示例所示)。遠程對象的 URL 是 http://localhost:8100/RecordingsServer。SAO 由本地主機上的框架在後臺自動創建。該 SAO 負責接受來自客戶端的請求,並在客戶端請求對象時創建這些對象。

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.ApplicationName = "RecordingsServer";

      RemotingConfiguration.RegisterActivatedServiceType(

         typeof(RecordingsManager));

      Console.WriteLine("Recordings Server Started");

      Console.ReadLine();

   }

}

HttpClient.cs

爲了能夠使用 new 運算符,並且使遠程處理框架創建一個遠程對象(與本地對象相反),必須首先將遠程對象的類型與服務器設置 ApplicationName 屬性時所指定的 URL 相關聯。該示例將 ApplicationName 定義爲 RecordingsServer,並且使用本地主機上的端口 8100。

using System;

using System.Data;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Http;

class HttpClient

{

   [STAThread]

   static void Main(string[] args)

   {

      HttpChannel channel = new HttpChannel();

      ChannelServices.RegisterChannel(channel);

      RemotingConfiguration.RegisterActivatedClientType(

         typeof(RecordingsManager), 

         "http://localhost:8100/RecordingsServer");

      RecordingsManager mgr = new RecordingsManager();

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

      DataSet ds = mgr.GetRecordings();

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

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

   }

}

註冊遠程對象會將該對象的類型與 URL 相關聯。之後,通過調用 new 在服務器上創建一個遠程對象。該對象看起來與代碼中的任何其他對象一樣。

這種實現允許在客戶端的控制下直接創建遠程對象。另外,這種實現還表明,在配置客戶端之後,創建對象的操作與使用 new 運算符在本地創建對象完全相同。不過,它有一個較大的缺點。這就是,您不能使用 SAO 模式中所描述的共享接口方式。這意味着,必須將編譯好的對象傳遞給客戶端。有關使用 SoapSuds 的其他替代方式,請參閱 Advanced .NET Remoting [Ingo02]。

注意:傳送經過編譯的服務器對象違反了分佈式對象的一般原則。另外,由於部署和版本控制問題,也不應該這樣做。

爲了解決一部分這樣的問題,下面的實現描述了混合法如何使用 SAO 創建對象。這種方式使客戶端能夠控制對象的生存期,而不必將服務器代碼傳送到客戶端。

混合法

混合法需要使用 RecordingsFactory SAO,它提供了創建 RecordingsManager CAO 的方法。(如果您不熟悉 SAO 示例,請參閱"使用服務器激活對象通過 .NET Remoting 實現 Broker"。)下面的類圖表描述了總體解決方案。

1: 混合法的結構

這種實現使用了 SAO 示例中所描述的共享接口法。IRecordingsManagerIRecordingsFactory 這兩個接口位於客戶端和服務器所共享的程序集中。IRecordingsFactory 有一個 Create 方法,它可以返回一個對象來實現 IRecordingsManager 接口。這是 AbstractFactory [Gamma95] 模式的一個例子。因爲客戶端只依靠接口,所以無需傳送服務器代碼。當客戶端需要 IRecordingsManager 對象時,它調用 IRecordingsFactory 實例的 Create 方法。這樣,客戶端就可以控制 IRecordingsManager 對象的生存期,而無需實現該對象。共享程序集中的兩個接口是:

IRecordingsManager.cs

以下示例顯示了 IRecordingsManager 接口:

using System;

using System.Data;

public interface IRecordingsManager

{

   DataSet GetRecordings();

}

IRecordingsFactory.cs

以下示例顯示了 IRecordingsFactory 接口:

using System;

public interface IRecordingsFactory

{

   IRecordingsManager Create();

}

這些對象的服務器實現(RecordingsFactoryRecordingsManager)非常簡單,並且包含在它們自己的、名爲 Server 的程序集中。

RecordingsFactory.cs

該類擴展了 MarshalByRefObject,並實現了 IRecordingsFactory 接口:

using System;

public class RecordingsFactory : MarshalByRefObject, IRecordingsFactory

{

   public IRecordingsManager Create()

   {

      return new RecordingsManager();

   }

}

RecordingsFactory 對象是服務器激活對象。該實現只是對 RecordingsManager 類型調用 new。該 RecordingsManager 對象是在服務器上創建的,並且,不是作爲 RecordingsManager 對象、而是作爲 IRecordingsManager 接口返回的。利用這種機制,客戶端就可以依賴於接口而不是實現。

RecordingsManager.cs

RecordingsManager 類所需要的唯一更改是,它現在實現的是 IRecordingsManager 接口。

using System;

using System.Reflection;

using System.Data;

using System.Data.SqlClient;

public class RecordingsManager : MarshalByRefObject, IRecordingsManager

{

   public DataSet GetRecordings()

   {

      Console.WriteLine("Assembly: {0} - filling a request",

         Assembly.GetEntryAssembly().GetName().Name);

      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;

   }

}

HttpServer.cs

混合法中的服務器初始化代碼用於爲服務器激活的 RecordingsFactory 對象配置遠程處理框架。激活方式與所使用的通道和協議無關,因此與以前一樣(端口 8100 上的 HTTP 協議)。

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(RecordingsFactory),

         "RecordingsFactory.soap",

         WellKnownObjectMode.Singleton);

      Console.ReadLine();

   }

}

在該代碼中,RecordingsFactory 類型與 URL http://localhost:8100/RecordingsFactory.soap 相關聯。

HttpClient.cs

客戶端代碼顯示了這種方式的混合性質。首先使用 Activator.GetObject 方法從服務器檢索 IRecordingsFactory 對象。然後,使用這個服務器激活對象來調用 Create 方法,以便實例化一個 IRecordingsManager 對象。這個新實例化的對象是在服務器上創建的,但它是一個遠程對象。

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);

      IRecordingsFactory factory = (IRecordingsFactory)

         Activator.GetObject(typeof(IRecordingsFactory),

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

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

      IRecordingsManager mgr = factory.Create();

      DataSet ds = mgr.GetRecordings();

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

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

   }

}

使用客戶端激活對象通過 .NET Remoting 來實現 Broker 具有以下優缺點:

優點

  • 分佈式對象模型。 .NET remoting 爲全功能的分佈式對象模型提供了運行在客戶端和服務器上的完整公共語言運行庫語義。客戶端和服務器之間所傳遞的數據的保真度不會受任何影響。該示例顯示瞭如何在客戶端和服務器之間傳遞複雜類型 System.Data.DataSet。如果連接的兩端沒有公共語言運行庫,就不可能實現這樣的傳遞。

  • 構造參數。客戶端激活實現和混合實現中的對象都考慮到了在創建對象時傳遞構造函數的參數。

缺點

  • 遠程對象。 您必須記住,這些對象是遠程對象。即使它們看上去像是本地對象,但從服務器來回封送數據仍然需要開銷。記住,遠程調用比公共語言運行庫中的本地調用至少慢 1000 倍。因此,您應當只在需要時才進行這樣的調用。由於需要最大限度地減少往返操作,這可能導致您在處理接口時不會使用最細的粒度。

  • 無共享程序集。在 CAO 方式中,不能使用共享程序集方式來處理接口。相反,必須將實現傳送到客戶端,或使用 SoapSuds 提取元數據。

  • 部署的複雜性。使用混合法中所描述的服務器激活對象時,在客戶端請求對象之前必須已經註冊了該對象。這會使部署變得更加複雜。

  • 有限的互操作性。 您可以使用 .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 的情況下駐留您的通道,則需要提供您自己的身份驗證、授權和隱私機制。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章