使用.NET Remoting开发分布式应用——基于租约的生存期(转载)
一.概述
知名類型的SingleCall對象可以在客戶程序的方法調用之后被垃圾收集器清理掉,因為它沒有保持狀態,屬于無狀態的。而客戶激活的類型的對象和知名類型的SingleTon對象都屬于生存期長的對象,如果在客戶程序停止使用遠程對象之前,遠程對象被禁用了,則客戶程序會得到一個RemotingException異常。因為該對象已經和下一個方法調用(從客戶程序進行的方法調用)斷開了連接,只要客戶程序需要該對象,它就必須被激活。
微軟的DCOM技術使用了Ping機制,在這種機制下,客戶程序有規律的對服務程序發出Ping請求,以通知服務程序自己仍舊活著,并通知服務程序自己需要使用哪個對象。.NET Remoting使用的是基于租約的生存期機制,在租約期內,對象一直存活著,直到租借時間結束,.NET Remoting使用Leasing程序完成了這項工作。
.NET Remoting允許我們通過一些方式來修改對象的租約時間,一種方式是編寫程序代碼來完成,另外一種方式是使用配置文件(有關配置文件的介紹可以參見《使用.NET Remoting開發分布式應用——配置文件篇》的內容),還有一種方式是通過發起人(Sponsor)來配置租約。先來看一下租約配置選項的默認值:
租約配置
默認值(秒)
LeaseTime
300
RenewOnCallTime
120
SponsorshipTimeout
120
LeaseManagerPollTime
10
使用LeaseTime選項可以定義遠程對象的最長租借時間。如果客戶程序一段時期內不再需要遠程對象了,那么該對象將被禁用。每次客戶程序使用遠程對象調用方法時,RenewOnCallTime定義的一個值會遞增租借時間。SponsorshipTimeout選項定義了在調用結束之前的那段默認時間,而LeaseManagerPollTime定義了發起人必須返回延長的那部分租借時間。
租約可以實現 ILease 接口并存儲一個屬性集合,用于確定更新的策略和方法。您也可以使用調用來更新租約。每次調用遠程對象上的方法時,租約時間都會設置為目前 LeaseTime 最大值加上 RenewOnCallTime。LeaseTime 即將過期時,發起者會被要求更新租約。因為我們有時會遇上網絡不穩定,所以可能會找不到租約發起者。為了確保不在服務器上留下無效對象,每個租約都帶有一個 SponsorshipTimeout。該值指定了租約終止之前等待租約發起者回復的時間長度。如果 SponsershipTimeout 為零,CurrentLeaseTime 會被用于確定租約的過期時間。如果 CurrentLeaseTime 的值為零,則租約不會過期。配置或 API 可用于替代 InitialLeaseTime、SponsorshipTimeout 和 RenewOnCallTime 的默認值。
租約管理器維護著一個按發起時間從大到小存儲的發起者列表(它們實現 ISponsor 接口)。需要調用發起者以更新租約時間時,租約管理器會從列表的頂部開始向一個或多個發起者要求更新租約時間。列表頂部的發起者表示其以前請求的租約更新時間最長。如果發起者沒有在 SponsorshipTimeOut 時間段內響應,則它會被從列表中刪除。通過調用 GetLifetimeService 并將對象租約作為參數,即可以獲得該對象租約。該調用是 RemotingServices 類的一個靜態方法。如果對象在應用程序域內部,則該調用的參數是對象的本地引用,且返回的租約也是該租約的本地引用。如果對象是遠程的,則代理會作為一個參數傳遞,且返回給調用方的是租約的透明代理。
二.通過配置文件配置租約
在服務器上的應用程序配置文件中編寫生存期的配置。在這種方式下,生存期配置對整個應用程序都有效。在應用程序配置文件的<lifttime>標記中,可以通過修改特性的方式來配置。
示例代碼:
?1<?xml?version="1.0"?encoding="utf-8"??>
?2<configuration>
?3????<system.runtime.remoting>
?4????????<application>
?5????????????<service>
?6????????????????<wellknown?
?7????????????????????mode="Singleton"?
?8????????????????????type="RemotingSamples.HelloServer,?General"?
?9????????????????????objectUri="SayHello"?/>
10????????????</service>
11????????????<channels>
12????????????????<channel?port="8086"?ref="http"/>
13????????????</channels>
14????????????
15????????????<lifetime?
16???????????????leaseTime="7M"?
17???????????????sponsorshipTimeout="7M"?
18???????????????renewOnCallTime="7M"
19???????????????leaseManagerPollTime="7S"
20???????????????/>
21????????</application>
22????</system.runtime.remoting>
23</configuration>
24
三.編寫代碼配置租約
如果我們需要一些帶有不同的生存期要求的遠程對象,那么最好是通過編程的方式來為對象設置生存期。在遠程對象中,可以覆蓋InitializeLifetimeService()方法。基類MarshalByRefObject中的InitializeLifetimeService()方法會返回一個對Ilease接口(該接口可用于修改默認值)的引用,因為只有在租約沒有生效的時候才可能修改默認值,所以,我們需要檢查租約的當前狀態,并把它和枚舉值LeaseState.Initial進行比較。
示例代碼:
?1?public?override?Object?InitializeLifetimeService()
?2????????{
?3
?4????????????ILease?lease?=?(ILease)base.InitializeLifetimeService();
?5????????????//?Normally,?the?initial?lease?time?would?be?much?longer.
?6????????????//?It?is?shortened?here?for?demonstration?purposes.
?7????????????if?(lease.CurrentState?==?LeaseState.Initial)
?8????????????{
?9????????????????lease.InitialLeaseTime?=?TimeSpan.FromSeconds(3);
10????????????????lease.SponsorshipTimeout?=?TimeSpan.FromSeconds(10);
11????????????????lease.RenewOnCallTime?=?TimeSpan.FromSeconds(2);
12????????????}
13????????????return?lease;
14????????}
租約的狀態LeaseState枚舉值如下表所示:
| 租約狀態的枚舉值 | 說明 |
| Active | 指明租約處于激活狀態 |
| Expired | 表明租約已經期滿,不能再恢復。當租約管理器發現對象上的租約已經期滿,它將聯系處于發起人列表中的租約發起人,決定是否恢復它的租約。如果發起人的響應超時,它將嘗試聯系發起人列表中的下一個發起人。如果租約管理器不能成功的從任何一個發起人那里獲得一個租約恢復響應,它將租約對象設置為Expired狀態。一旦如此,租約對象就不能再復活,只能被垃圾收集器收集 |
| Initial | 表明租約還沒有被創建,但仍然沒有被激活 |
| Null | 租約還沒有被初始化 |
| Renewing | 表明租約已經期滿,租約管理器正在尋找發起人。這個狀態指出租約管理器正在嘗試聯系已經為這個對象的租約恢復而注冊的租約發起人 |
?
只有當租約處于初始狀態時,才可以更改租約屬性。InitializeLifetimeService 的實現通常調用基類的相應方法,來檢索遠程對象的現有租約。如果在此之前從未對該對象封送過,則返回的租約會處于其初始狀態且可以設置租約屬性。一旦封送了對象,則租約會從初始狀態變為激活狀態,并忽略任何初始化租約屬性的嘗試(但有一種情況例外)。激活遠程對象時將調用 InitializeLifetimeService。通過激活調用可以提供一個租約發起者的列表,而且當租約處于激活狀態時,可以隨時將其他發起者添加到列表中。
可以下列方式延長租約時間:
- 客戶端可以調用 Lease 類上的 Renew 方法。
- 租約可以向某個發起者請求 Renewal。
- 當客戶端調用對象上的某個方法時,RenewOnCall 值會自動更新租約。
一旦租約過期,其內部狀態會由 Active 變為 Expired,且不再對發起者進行任何調用,對象也會被作為垃圾回收。一般情況下,如果發起者分散在 Web 上或位于某個防火墻的后面,遠程對象回叫發起者時會遇到困難。因此,發起者不必與客戶端處于同一位置,只要遠程對象能夠訪問得到,它可以為網絡上的任意位置。
四.通過發起者來配置租約
我們也可以通過發起者來修改生存期服務數值。通過發起者配置,.NET Remoting運行時使用ISponsor接口來延長遠程對象的生存期,ISponsor定義了Renewal()方法,.NET Remoting的基礎結構會調用該方法來延長當前對象的租借時間。使用租約參數,可以讀取當前租約的配置和租借時間的實際情況。我們必須使用返回值為對象定義額外的租借時間。在下面的示例代碼中,創建了一個發起者,并修改它的相關的配置參數。
示例代碼:
?1using?System;
?2using?System.Runtime.Remoting;
?3using?System.Runtime.Remoting.Channels;
?4using?System.Runtime.Remoting.Channels.Tcp;
?5using?System.Runtime.Remoting.Channels.Http;
?6using?System.Runtime.Remoting.Activation;
?7using?System.Runtime.Remoting.Lifetime;
?8using?System.IO;
?9
10namespace?RemotingSamples?
11{
12????public?class?Client
13????{
14????????public?static?void?Main(string[]?args)
15????????{
16????????????//使用TCP通道得到遠程對象
17????????????ChannelServices.RegisterChannel(new?HttpChannel());
18
19????????????HelloServer?obj?=?(HelloServer)Activator.GetObject(
20??????????????typeof(RemotingSamples.HelloServer),
21??????????????"http://localhost:8086/SayHello");
22????????????if?(obj?==?null)
23????????????{
24????????????????System.Console.WriteLine(
25????????????????????"Could?not?locate?HTTP?server");
26????????????}
27????????????
28
29????????????MySponsor?sponsor?=?new?MySponsor();
30????????????sponsor.RenewalTime?=?TimeSpan.FromMinutes(2);
31????????????sponsor.Register(obj);
32
33????????????ILease?lease?=?(ILease)obj.GetLifetimeService();
34????????????if?(lease?!=?null)
35????????????{
36????????????????Console.WriteLine("Lease?Configuration:");
37????????????????Console.WriteLine("InitialLeaseTime:?"?+
38????????????????????lease.InitialLeaseTime);
39????????????????Console.WriteLine("RenewOnCallTime:?"?+
40????????????????????lease.RenewOnCallTime);
41????????????????Console.WriteLine("SponsorshipTimeout:?"?+
42????????????????????lease.SponsorshipTimeout);
43????????????????Console.WriteLine(lease.CurrentLeaseTime);
44????????????}
45
46????????}
47
48????}
49
50????public?class?MySponsor:ClientSponsor,ISponsor
51????{
52????????TimeSpan?ISponsor.Renewal(ILease?lease)
53????????{
54????????????Console.WriteLine("Renewal?called");
55
56????????????return?this.RenewalTime;
57????????}
58????}
59}
60
五.總結
通過租約來管理遠程對象的生存期可以作為引用計數的一種替代方法,因為當網絡連接的性能不可靠時,引用計數會顯得復雜和低效。盡管有人會堅持認為遠程對象的生存期比所需的時間要長,但與引用計數和連接客戶相比,租約降低了網絡的繁忙程度,將會成為一種非常受歡迎的解決方案。
?
附錄:一個完整的用程序代碼配置租約生存期的例子
?Server.cs
?1using?System;
?2using?System.Runtime.Remoting;
?3using?System.Runtime.Remoting.Channels;
?4using?System.Runtime.Remoting.Channels.Tcp;
?5using?System.Runtime.Remoting.Channels.Http;
?6
?7namespace?RemotingSamples?
?8{
?9
10????public?class?Server
11????{
12????????public?static?int?Main(string?[]?args)?
13????????{
14
15
16?????????????TcpChannel?chan1?=?new?TcpChannel(8085);
17????????????HttpChannel?chan2?=?new?HttpChannel(8086);
18
19????????????ChannelServices.RegisterChannel(chan1);
20????????????ChannelServices.RegisterChannel(chan2);
21
22????????????//服務器端激活。
23????????????RemotingConfiguration.RegisterWellKnownServiceType
24????????????????(
25????????????????typeof(HelloServer),
26????????????????"SayHello",
27????????????????WellKnownObjectMode.Singleton
28????????????????);??????
29
30????????????System.Console.WriteLine("Press?Enter?key?to?exit");
31????????????System.Console.ReadLine();
32????????????return?0;
33????????}
34
35????}
36}
37
HelloWord.cs
?2using?System.Collections.Generic;
?3using?System.Text;
?4using?System.Runtime.Remoting.Lifetime;
?5
?6namespace?RemotingSamples
?7{
?8????public?class?HelloServer?:?MarshalByRefObject
?9????{
10????????public?HelloServer()
11????????{
12????????????Console.WriteLine("HelloServer?activated");
13????????}
14????????public?String?HelloMethod(String?name)
15????????{
16????????????Console.WriteLine(
17????????????????"Server?Hello.HelloMethod?:?{0}",?name);
18????????????return?"Hi?there?"?+?name;
19????????}
20
21????????//?Overrides?the?lease?settings?for?this?object.
22????????public?override?object?InitializeLifetimeService()
23????????{
24
25????????????ILease?lease?=?(ILease)base.InitializeLifetimeService();
26????????????//?Normally,?the?initial?lease?time?would?be?much?longer.
27????????????//?It?is?shortened?here?for?demonstration?purposes.
28????????????if?(lease.CurrentState?==?LeaseState.Initial)
29????????????{
30????????????????lease.InitialLeaseTime?=?TimeSpan.FromSeconds(3);
31????????????????lease.SponsorshipTimeout?=?TimeSpan.FromSeconds(10);
32????????????????lease.RenewOnCallTime?=?TimeSpan.FromSeconds(2);
33????????????}
34????????????return?lease;
35????????}
36
37????}
38}
39
40????????
41
42
43
Client.cs
?
?2using?System.Runtime.Remoting;
?3using?System.Runtime.Remoting.Channels;
?4using?System.Runtime.Remoting.Channels.Tcp;
?5using?System.Runtime.Remoting.Channels.Http;
?6using?System.Runtime.Remoting.Activation;
?7using?System.Runtime.Remoting.Lifetime;
?8using?System.IO;
?9
10namespace?RemotingSamples?
11{
12????public?class?Client
13????{
14????????public?static?void?Main(string[]?args)
15????????{
16????????????//使用TCP通道得到遠程對象
17????????????ChannelServices.RegisterChannel(new?HttpChannel());
18
19????????????HelloServer?obj?=?(HelloServer)Activator.GetObject(
20??????????????typeof(RemotingSamples.HelloServer),
21??????????????"http://localhost:8086/SayHello");
22????????????if?(obj?==?null)
23????????????{
24????????????????System.Console.WriteLine(
25????????????????????"Could?not?locate?HTTP?server");
26????????????}
27????????????
28
29????????????ILease?lease?=?(ILease)obj.GetLifetimeService();
30????????????if?(lease?!=?null)
31????????????{
32????????????????Console.WriteLine("Lease?Configuration:");
33????????????????Console.WriteLine("InitialLeaseTime:?"?+
34????????????????????lease.InitialLeaseTime);
35????????????????Console.WriteLine("RenewOnCallTime:?"?+
36????????????????????lease.RenewOnCallTime);
37????????????????Console.WriteLine("SponsorshipTimeout:?"?+
38????????????????????lease.SponsorshipTimeout);
39????????????????Console.WriteLine(lease.CurrentLeaseTime);
40????????????}
41
42????????}
43
44????}
45
46}
47
總結
以上是生活随笔為你收集整理的使用.NET Remoting开发分布式应用——基于租约的生存期(转载)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到自己房间着火了预兆什么
- 下一篇: ASP.NET 2.0 Club Web