遠程對象的啟用模式分
服務端啟用和
用戶端啟用兩種,(也就是對象分服務端啟用物件或者說是知名對象和用戶端啟用物件兩種)先看看msdn怎麼描述服務端啟用的:
伺服器啟用的對象是其生存期由伺服器直接控制的對象。伺服器應用程式域只有在用戶端在對象上進行方法調用時才建立這些對象,而不會在用戶端調用 new 或 Activator.GetObject 時建立這些對象;這節省了僅為建立執行個體而進行的一次網路往返過程。用戶端請求伺服器啟用的類型執行個體時,只在用戶端應用程式域中建立一個代理。然而,這也意味著當您使用預設實現時,只允許對伺服器啟用的類型使用預設建構函式。若要發布其執行個體將使用帶參數的特定建構函式建立的類型,可以使用用戶端啟用或者動態地發布您的特定執行個體。
伺服器啟用的對象有兩種啟用模式(或 WellKnownObjectMode 值):Singleton 和 SingleCall。
Singleton 類型任何時候都不會同時具有多個執行個體。如果存在執行個體,所有用戶端請求都由該執行個體提供服務。如果不存在執行個體,伺服器將建立一個執行個體,而所有後繼的用戶端請求都將由該執行個體來提供服務。由於 Singleton 類型具有關聯的預設生存期,即使任何時候都不會有一個以上的可用執行個體,用戶端也不會總接收到對可遠端的類的同一執行個體的引用。
SingleCall 遠程伺服器類型總是為每個用戶端請求設定一個執行個體。下一個方法調用將改由其他執行個體進行服務。從設計角度看,SingleCall 類型提供的功能非常簡單。這種機制不提供狀態管理,如果您需要狀態管理,這將是一個不利之處;如果您不需要,這種機制將非常理想。也許您只關心Server Load Balancer和延展性而不關心狀態,那麼在這種情況下,這種模式將是您理想的選擇,因為對於每個請求都只有一個執行個體。如果願意,開發人員可以向 SingleCall 對象提供自己的狀態管理,但這種狀態資料不會駐留在對象中,因為每次調用新的方法時都將執行個體化一個新的對象標識。
首先對於服務端啟用的兩種模式來做一個實驗,我們把遠程對象做如下的修改:
using System;
namespace RemoteObject
{
public class MyObject:MarshalByRefObject
{
private int i=0;
public int Add(int a,int b)
{
return a+b;
}
public int Count()
{
return ++i;
}
}
}
對用戶端做以下修改:RemoteObject.MyObject app = (RemoteObject.MyObject)Activator.GetObject(typeof(RemoteObject.MyObject),System.Configuration.ConfigurationSettings.AppSettings["ServiceURL"]);
Console.WriteLine(app.Count());
Console.ReadLine();
第一次開啟用戶端的時候顯示1,第二次開啟的時候顯示2,類推……由此驗證了Singleton 類型任何時候都不會同時具有多個執行個體。如果存在執行個體,所有用戶端請求都由該執行個體提供服務。如果不存在執行個體,伺服器將建立一個執行個體,而所有後繼的用戶端請求都將由該執行個體來提供服務。
把伺服器端的config修改一下:
<wellknown type="RemoteObject.MyObject,RemoteObject" objectUri="RemoteObject.MyObject"
mode="SingleCall" />
(這裡注意大小寫,大寫的C)
再重新運行服務端和用戶端,開啟多個用戶端發現始終顯示1。由此驗證了SingleCall 類型對於每個用戶端請求都會重新建立執行個體。下一個方法調用將由另一個伺服器執行個體提供服務。
下面再說一下用戶端的啟用模式,msdn中這麼寫:
用戶端啟用的對象是其生存期由調用應用程式定義域控制的對象,正如對象對於用戶端是本機物件時對象的生存期由調用應用程式定義域控制一樣。對於用戶端啟用,當用戶端試圖建立伺服器對象的執行個體時發生一個到伺服器的往返過程,而用戶端代理是使用對象引用 (ObjRef) 建立的,該對象引用是從在伺服器上建立遠程對象返回時擷取的。每當用戶端建立用戶端啟用的類型的執行個體時,該執行個體都將只服務於該特定用戶端中的特定引用,直到其租約到期並回收其記憶體為止。如果調用應用程式定義域建立兩個遠程類型的新執行個體,每個用戶端引用都將只調用從其中返回引用的伺服器應用程式域中的特定執行個體。
理解一下,可以歸納出
1、用戶端啟用的時間是在用戶端請求的時候,而服務端啟用遠程對象的時間是在調用對象方法的時候
遠程對象修改如下:using System;
namespace RemoteObject
{
public class MyObject:MarshalByRefObject
{
private int i=0;
public MyObject()
{
Console.WriteLine("啟用");
}
public int Add(int a,int b)
{
return a+b;
}
public int Count()
{
return ++i;
}
}
}
服務端設定檔:<configuration>
<system.runtime.remoting>
<application name="RemoteServer">
<service>
<activated type="RemoteObject.MyObject,RemoteObject"/>
</service>
<channels>
<channel ref="tcp" port="9999"/>
</channels>
</application>
</system.runtime.remoting>
</configuration>
用戶端程式:using System;
namespace RemoteClient
{
class MyClient
{
[STAThread]
static void Main(string[] args)
{
//RemoteObject.MyObject app = (RemoteObject.MyObject)Activator.GetObject(typeof(RemoteObject.MyObject),System.Configuration.ConfigurationSettings.AppSettings["ServiceURL"]);
RemoteObject.MyObject app=(RemoteObject.MyObject)Activator.CreateInstance(typeof(RemoteObject.MyObject),null,new object[]{new System.Runtime.Remoting.Activation.UrlAttribute(System.Configuration.ConfigurationSettings.AppSettings["ServiceURL"])});
//Console.WriteLine(app.Count());
Console.ReadLine();
}
}
}
用戶端設定檔:<configuration>
<appSettings>
<add key="ServiceURL" value="tcp://localhost:9999/RemoteServer"/>
</appSettings>
</configuration>
(這裡的uri按照服務端設定檔中application元素定義的RemoteServer來寫)
運行程式可以看到,在用戶端啟動的時候服務端就輸出了“啟用”,我們再轉回知名模式進行測試發現只有運行了方法才會在服務端輸出“啟用”。
2、用戶端啟用可以調用自訂的構造方法,而不像服務端啟用只能使用預設的構造方法
把用戶端代碼修改如下:RemoteObject.MyObject app=(RemoteObject.MyObject)Activator.CreateInstance(typeof(RemoteObject.MyObject),new object[]{10},new object[]{new System.Runtime.Remoting.Activation.UrlAttribute(System.Configuration.ConfigurationSettings.AppSettings["ServiceURL"])});
Console.WriteLine(app.Count());
這裡看到我們在CreateInstance方法的第二個參數中提供了10作為構造方法的參數。在服務端啟用模式我們不能這麼做。
遠程物件建構方法修改如下:
public MyObject(int k)
{
this.i=k;
Console.WriteLine("啟用");
}
毫無疑問,我們運行用戶端發現輸出的是11而不是1了。
3、通過上面的例子,我們運行多個用戶端發現出現的永遠是11,因此,用戶端啟用模式一旦獲得用戶端的請求,將為每一個用戶端都建立一個執行個體引用。
總結:
1、Remoting支援兩種遠程對象:知名的和客戶啟用的。知名的遠程對象使用了uri作為標識,客戶程式使用這個uri來訪問那些遠程對象,也正式為什麼稱作知名的原因。對知名的對象來說2種使用模式:SingleCall和Singleton,對於前者每次調用都會建立對象,因此對象是無狀態的。對於後者,對象只被建立一次,所有客戶共用對象狀態,因此對象是有狀態的。另外一種用戶端啟用物件使用類的類型來啟用,uri再後台被動態建立,並且返回給客戶程式。客戶啟用物件是有狀態的。
2、對於Singleton對象來說需要考慮伸縮性,Singleton對象不能在多個伺服器上被部署,如果要跨伺服器就不能使用Singleton了。
備忘:個人習慣原因,在我的例子中服務端的配置都是用config檔案的,用戶端的配置都是基本用程式方式的
使用設定檔的優點:無需重新編譯就可以配置通道和遠程對象,編寫的代碼量比較少
使用程式定製的優點:可以獲得運行期間的資訊,對程式調試有利。
附:msdn有關章節:
http://msdn.microsoft.com/library/CHS/cpguide/html/cpconActivation.asp