Remoting服務端和客戶端程式該這樣模式來寫

javaprogramers發表於2006-05-14
遠端物件的啟用模式分服務端啟用客戶端啟用兩種,(也就是物件分服務端啟用物件或者說是知名物件和客戶端啟用物件兩種)先看看msdn怎麼描述服務端啟用的:

伺服器啟用的物件是其生存期由伺服器直接控制的物件。伺服器應用程式域只有在客戶端在物件上進行方法呼叫時才建立這些物件,而不會在客戶端呼叫 new 或 Activator.GetObject 時建立這些物件;這節省了僅為建立例項而進行的一次網路往返過程。客戶端請求伺服器啟用的型別例項時,只在客戶端應用程式域中建立一個代理。然而,這也意味著當您使用預設實現時,只允許對伺服器啟用的型別使用預設建構函式。若要釋出其例項將使用帶引數的特定建構函式建立的型別,可以使用客戶端啟用或者動態地釋出您的特定例項。

伺服器啟用的物件有兩種啟用模式(或 WellKnownObjectMode 值):Singleton 和 SingleCall。

Singleton 型別任何時候都不會同時具有多個例項。如果存在例項,所有客戶端請求都由該例項提供服務。如果不存在例項,伺服器將建立一個例項,而所有後繼的客戶端請求都將由該例項來提供服務。由於 Singleton 型別具有關聯的預設生存期,即使任何時候都不會有一個以上的可用例項,客戶端也不會總接收到對可遠端處理的類的同一例項的引用。

SingleCall 遠端伺服器型別總是為每個客戶端請求設定一個例項。下一個方法呼叫將改由其他例項進行服務。從設計角度看,SingleCall 型別提供的功能非常簡單。這種機制不提供狀態管理,如果您需要狀態管理,這將是一個不利之處;如果您不需要,這種機制將非常理想。也許您只關心負載平衡和可伸縮性而不關心狀態,那麼在這種情況下,這種模式將是您理想的選擇,因為對於每個請求都只有一個例項。如果願意,開發人員可以向 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

相關文章