第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 的狀況下駐留您的通道,則須要提供您本身的身份驗證、受權和隱私機制。

相關文章
相關標籤/搜索