使用.NET Remoting开发分布式应用——基于租约的生存期(转载)
一.概述
知名類型的SingleCall對(duì)象可以在客戶程序的方法調(diào)用之后被垃圾收集器清理掉,因?yàn)樗鼪]有保持狀態(tài),屬于無狀態(tài)的。而客戶激活的類型的對(duì)象和知名類型的SingleTon對(duì)象都屬于生存期長的對(duì)象,如果在客戶程序停止使用遠(yuǎn)程對(duì)象之前,遠(yuǎn)程對(duì)象被禁用了,則客戶程序會(huì)得到一個(gè)RemotingException異常。因?yàn)樵搶?duì)象已經(jīng)和下一個(gè)方法調(diào)用(從客戶程序進(jìn)行的方法調(diào)用)斷開了連接,只要客戶程序需要該對(duì)象,它就必須被激活。
微軟的DCOM技術(shù)使用了Ping機(jī)制,在這種機(jī)制下,客戶程序有規(guī)律的對(duì)服務(wù)程序發(fā)出Ping請(qǐng)求,以通知服務(wù)程序自己仍舊活著,并通知服務(wù)程序自己需要使用哪個(gè)對(duì)象。.NET Remoting使用的是基于租約的生存期機(jī)制,在租約期內(nèi),對(duì)象一直存活著,直到租借時(shí)間結(jié)束,.NET Remoting使用Leasing程序完成了這項(xiàng)工作。
.NET Remoting允許我們通過一些方式來修改對(duì)象的租約時(shí)間,一種方式是編寫程序代碼來完成,另外一種方式是使用配置文件(有關(guān)配置文件的介紹可以參見《使用.NET Remoting開發(fā)分布式應(yīng)用——配置文件篇》的內(nèi)容),還有一種方式是通過發(fā)起人(Sponsor)來配置租約。先來看一下租約配置選項(xiàng)的默認(rèn)值:
租約配置
默認(rèn)值(秒)
LeaseTime
300
RenewOnCallTime
120
SponsorshipTimeout
120
LeaseManagerPollTime
10
使用LeaseTime選項(xiàng)可以定義遠(yuǎn)程對(duì)象的最長租借時(shí)間。如果客戶程序一段時(shí)期內(nèi)不再需要遠(yuǎn)程對(duì)象了,那么該對(duì)象將被禁用。每次客戶程序使用遠(yuǎn)程對(duì)象調(diào)用方法時(shí),RenewOnCallTime定義的一個(gè)值會(huì)遞增租借時(shí)間。SponsorshipTimeout選項(xiàng)定義了在調(diào)用結(jié)束之前的那段默認(rèn)時(shí)間,而LeaseManagerPollTime定義了發(fā)起人必須返回延長的那部分租借時(shí)間。
租約可以實(shí)現(xiàn) ILease 接口并存儲(chǔ)一個(gè)屬性集合,用于確定更新的策略和方法。您也可以使用調(diào)用來更新租約。每次調(diào)用遠(yuǎn)程對(duì)象上的方法時(shí),租約時(shí)間都會(huì)設(shè)置為目前 LeaseTime 最大值加上 RenewOnCallTime。LeaseTime 即將過期時(shí),發(fā)起者會(huì)被要求更新租約。因?yàn)槲覀冇袝r(shí)會(huì)遇上網(wǎng)絡(luò)不穩(wěn)定,所以可能會(huì)找不到租約發(fā)起者。為了確保不在服務(wù)器上留下無效對(duì)象,每個(gè)租約都帶有一個(gè) SponsorshipTimeout。該值指定了租約終止之前等待租約發(fā)起者回復(fù)的時(shí)間長度。如果 SponsershipTimeout 為零,CurrentLeaseTime 會(huì)被用于確定租約的過期時(shí)間。如果 CurrentLeaseTime 的值為零,則租約不會(huì)過期。配置或 API 可用于替代 InitialLeaseTime、SponsorshipTimeout 和 RenewOnCallTime 的默認(rèn)值。
租約管理器維護(hù)著一個(gè)按發(fā)起時(shí)間從大到小存儲(chǔ)的發(fā)起者列表(它們實(shí)現(xiàn) ISponsor 接口)。需要調(diào)用發(fā)起者以更新租約時(shí)間時(shí),租約管理器會(huì)從列表的頂部開始向一個(gè)或多個(gè)發(fā)起者要求更新租約時(shí)間。列表頂部的發(fā)起者表示其以前請(qǐng)求的租約更新時(shí)間最長。如果發(fā)起者沒有在 SponsorshipTimeOut 時(shí)間段內(nèi)響應(yīng),則它會(huì)被從列表中刪除。通過調(diào)用 GetLifetimeService 并將對(duì)象租約作為參數(shù),即可以獲得該對(duì)象租約。該調(diào)用是 RemotingServices 類的一個(gè)靜態(tài)方法。如果對(duì)象在應(yīng)用程序域內(nèi)部,則該調(diào)用的參數(shù)是對(duì)象的本地引用,且返回的租約也是該租約的本地引用。如果對(duì)象是遠(yuǎn)程的,則代理會(huì)作為一個(gè)參數(shù)傳遞,且返回給調(diào)用方的是租約的透明代理。
二.通過配置文件配置租約
在服務(wù)器上的應(yīng)用程序配置文件中編寫生存期的配置。在這種方式下,生存期配置對(duì)整個(gè)應(yīng)用程序都有效。在應(yīng)用程序配置文件的<lifttime>標(biāo)記中,可以通過修改特性的方式來配置。
示例代碼:
?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
三.編寫代碼配置租約
如果我們需要一些帶有不同的生存期要求的遠(yuǎn)程對(duì)象,那么最好是通過編程的方式來為對(duì)象設(shè)置生存期。在遠(yuǎn)程對(duì)象中,可以覆蓋InitializeLifetimeService()方法。基類MarshalByRefObject中的InitializeLifetimeService()方法會(huì)返回一個(gè)對(duì)Ilease接口(該接口可用于修改默認(rèn)值)的引用,因?yàn)橹挥性谧饧s沒有生效的時(shí)候才可能修改默認(rèn)值,所以,我們需要檢查租約的當(dāng)前狀態(tài),并把它和枚舉值LeaseState.Initial進(jìn)行比較。
示例代碼:
?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????????}
租約的狀態(tài)LeaseState枚舉值如下表所示:
| 租約狀態(tài)的枚舉值 | 說明 | 
| Active | 指明租約處于激活狀態(tài) | 
| Expired | 表明租約已經(jīng)期滿,不能再恢復(fù)。當(dāng)租約管理器發(fā)現(xiàn)對(duì)象上的租約已經(jīng)期滿,它將聯(lián)系處于發(fā)起人列表中的租約發(fā)起人,決定是否恢復(fù)它的租約。如果發(fā)起人的響應(yīng)超時(shí),它將嘗試聯(lián)系發(fā)起人列表中的下一個(gè)發(fā)起人。如果租約管理器不能成功的從任何一個(gè)發(fā)起人那里獲得一個(gè)租約恢復(fù)響應(yīng),它將租約對(duì)象設(shè)置為Expired狀態(tài)。一旦如此,租約對(duì)象就不能再復(fù)活,只能被垃圾收集器收集 | 
| Initial | 表明租約還沒有被創(chuàng)建,但仍然沒有被激活 | 
| Null | 租約還沒有被初始化 | 
| Renewing | 表明租約已經(jīng)期滿,租約管理器正在尋找發(fā)起人。這個(gè)狀態(tài)指出租約管理器正在嘗試聯(lián)系已經(jīng)為這個(gè)對(duì)象的租約恢復(fù)而注冊(cè)的租約發(fā)起人 | 
?
只有當(dāng)租約處于初始狀態(tài)時(shí),才可以更改租約屬性。InitializeLifetimeService 的實(shí)現(xiàn)通常調(diào)用基類的相應(yīng)方法,來檢索遠(yuǎn)程對(duì)象的現(xiàn)有租約。如果在此之前從未對(duì)該對(duì)象封送過,則返回的租約會(huì)處于其初始狀態(tài)且可以設(shè)置租約屬性。一旦封送了對(duì)象,則租約會(huì)從初始狀態(tài)變?yōu)榧せ顮顟B(tài),并忽略任何初始化租約屬性的嘗試(但有一種情況例外)。激活遠(yuǎn)程對(duì)象時(shí)將調(diào)用 InitializeLifetimeService。通過激活調(diào)用可以提供一個(gè)租約發(fā)起者的列表,而且當(dāng)租約處于激活狀態(tài)時(shí),可以隨時(shí)將其他發(fā)起者添加到列表中。
可以下列方式延長租約時(shí)間:
- 客戶端可以調(diào)用 Lease 類上的 Renew 方法。
- 租約可以向某個(gè)發(fā)起者請(qǐng)求 Renewal。
- 當(dāng)客戶端調(diào)用對(duì)象上的某個(gè)方法時(shí),RenewOnCall 值會(huì)自動(dòng)更新租約。
一旦租約過期,其內(nèi)部狀態(tài)會(huì)由 Active 變?yōu)?span lang="en-us"> Expired,且不再對(duì)發(fā)起者進(jìn)行任何調(diào)用,對(duì)象也會(huì)被作為垃圾回收。一般情況下,如果發(fā)起者分散在 Web 上或位于某個(gè)防火墻的后面,遠(yuǎn)程對(duì)象回叫發(fā)起者時(shí)會(huì)遇到困難。因此,發(fā)起者不必與客戶端處于同一位置,只要遠(yuǎn)程對(duì)象能夠訪問得到,它可以為網(wǎng)絡(luò)上的任意位置。
四.通過發(fā)起者來配置租約
我們也可以通過發(fā)起者來修改生存期服務(wù)數(shù)值。通過發(fā)起者配置,.NET Remoting運(yùn)行時(shí)使用ISponsor接口來延長遠(yuǎn)程對(duì)象的生存期,ISponsor定義了Renewal()方法,.NET Remoting的基礎(chǔ)結(jié)構(gòu)會(huì)調(diào)用該方法來延長當(dāng)前對(duì)象的租借時(shí)間。使用租約參數(shù),可以讀取當(dāng)前租約的配置和租借時(shí)間的實(shí)際情況。我們必須使用返回值為對(duì)象定義額外的租借時(shí)間。在下面的示例代碼中,創(chuàng)建了一個(gè)發(fā)起者,并修改它的相關(guān)的配置參數(shù)。
示例代碼:
?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通道得到遠(yuǎn)程對(duì)象
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
五.總結(jié)
通過租約來管理遠(yuǎn)程對(duì)象的生存期可以作為引用計(jì)數(shù)的一種替代方法,因?yàn)楫?dāng)網(wǎng)絡(luò)連接的性能不可靠時(shí),引用計(jì)數(shù)會(huì)顯得復(fù)雜和低效。盡管有人會(huì)堅(jiān)持認(rèn)為遠(yuǎn)程對(duì)象的生存期比所需的時(shí)間要長,但與引用計(jì)數(shù)和連接客戶相比,租約降低了網(wǎng)絡(luò)的繁忙程度,將會(huì)成為一種非常受歡迎的解決方案。
?
附錄:一個(gè)完整的用程序代碼配置租約生存期的例子
?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????????????//服務(wù)器端激活。
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通道得到遠(yuǎn)程對(duì)象
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
總結(jié)
以上是生活随笔為你收集整理的使用.NET Remoting开发分布式应用——基于租约的生存期(转载)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 梦到自己房间着火了预兆什么
- 下一篇: 我的blog开张了,希望大家能多多赏光啊
