一.概述html
知名类型的SingleCall对象能够在客户程序的方法调用以后被垃圾收集器清理掉,由于它没有保持状态,属于无状态的。而客户激活的类型的对象和知名类型的SingleTon对象都属于生存期长的对象,若是在客户程序中止使用远程对象以前,远程对象被禁用了,则客户程序会获得一个RemotingException异常。由于该对象已经和下一个方法调用(从客户程序进行的方法调用)断开了链接,只要客户程序须要该对象,它就必须被激活。编程
微软的DCOM技术使用了Ping机制,在这种机制下,客户程序有规律的对服务程序发出Ping请求,以通知服务程序本身仍旧活着,并通知服务程序本身须要使用哪一个对象。.NET Remoting使用的是基于租约的生存期机制,在租约期内,对象一直存活着,直到租借时间结束,.NET Remoting使用Leasing程序完成了这项工做。服务器
.NET Remoting容许咱们经过一些方式来修改对象的租约时间,一种方式是编写程序代码来完成,另一种方式是使用配置文件(有关配置文件的介绍能够参见《使用.NET Remoting开发分布式应用——配置文件篇》的内容),还有一种方式是经过发起人(Sponsor)来配置租约。先来看一下租约配置选项的默认值:网络
租约配置app |
默认值(秒)分布式 |
LeaseTimeide |
300性能 |
RenewOnCallTimethis |
120spa |
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>标记中,能够经过修改特性的方式来配置。
示例代码:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.runtime.remoting> <application> <service> <wellknown mode="Singleton" type="RemotingSamples.HelloServer, General" objectUri="SayHello" /> </service> <channels> <channel port="8086" ref="http"/> </channels> <lifetime leaseTime="7M" sponsorshipTimeout="7M" renewOnCallTime="7M" leaseManagerPollTime="7S" /> </application> </system.runtime.remoting> </configuration>
三.编写代码配置租约
若是咱们须要一些带有不一样的生存期要求的远程对象,那么最好是经过编程的方式来为对象设置生存期。在远程对象中,能够覆盖InitializeLifetimeService()方法。基类MarshalByRefObject中的InitializeLifetimeService()方法会返回一个对Ilease接口(该接口可用于修改默认值)的引用,由于只有在租约没有生效的时候才可能修改默认值,因此,咱们须要检查租约的当前状态,并把它和枚举值LeaseState.Initial进行比较。
示例代码:
public override Object InitializeLifetimeService() { ILease lease = (ILease)base.InitializeLifetimeService(); // Normally, the initial lease time would be much longer. // It is shortened here for demonstration purposes. if (lease.CurrentState == LeaseState.Initial) { lease.InitialLeaseTime = TimeSpan.FromSeconds(3); lease.SponsorshipTimeout = TimeSpan.FromSeconds(10); lease.RenewOnCallTime = TimeSpan.FromSeconds(2); } return lease; }
租约的状态LeaseState枚举值以下表所示:
租约状态的枚举值 |
说明 |
Active |
指明租约处于激活状态 |
Expired |
代表租约已经期满,不能再恢复。当租约管理器发现对象上的租约已经期满,它将联系处于发起人列表中的租约发起人,决定是否恢复它的租约。若是发起人的响应超时,它将尝试联系发起人列表中的下一个发起人。若是租约管理器不能成功的从任何一个发起人那里得到一个租约恢复响应,它将租约对象设置为Expired状态。一旦如此,租约对象就不能再复活,只能被垃圾收集器收集 |
Initial |
代表租约尚未被建立,但仍然没有被激活 |
Null |
租约尚未被初始化 |
Renewing |
代表租约已经期满,租约管理器正在寻找发起人。这个状态指出租约管理器正在尝试联系已经为这个对象的租约恢复而注册的租约发起人 |
只有当租约处于初始状态时,才能够更改租约属性。InitializeLifetimeService 的实现一般调用基类的相应方法,来检索远程对象的现有租约。若是在此以前从未对该对象封送过,则返回的租约会处于其初始状态且能够设置租约属性。一旦封送了对象,则租约会从初始状态变为激活状态,并忽略任何初始化租约属性的尝试(但有一种状况例外)。激活远程对象时将调用 InitializeLifetimeService。经过激活调用能够提供一个租约发起者的列表,并且当租约处于激活状态时,能够随时将其余发起者添加到列表中。
能够下列方式延长租约时间:
一旦租约过时,其内部状态会由 Active 变为 Expired,且再也不对发起者进行任何调用,对象也会被做为垃圾回收。通常状况下,若是发起者分散在 Web 上或位于某个防火墙的后面,远程对象回叫发起者时会遇到困难。所以,发起者没必要与客户端处于同一位置,只要远程对象可以访问获得,它能够为网络上的任意位置。
四.经过发起者来配置租约
咱们也能够经过发起者来修改生存期服务数值。经过发起者配置,.NET Remoting运行时使用ISponsor接口来延长远程对象的生存期,ISponsor定义了Renewal()方法,.NET Remoting的基础结构会调用该方法来延长当前对象的租借时间。使用租约参数,能够读取当前租约的配置和租借时间的实际状况。咱们必须使用返回值为对象定义额外的租借时间。在下面的示例代码中,建立了一个发起者,并修改它的相关的配置参数。
示例代码:
using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Remoting.Activation; using System.Runtime.Remoting.Lifetime; using System.IO; namespace RemotingSamples { public class Client { public static void Main(string[] args) { //使用TCP通道获得远程对象 ChannelServices.RegisterChannel(new HttpChannel()); HelloServer obj = (HelloServer)Activator.GetObject( typeof(RemotingSamples.HelloServer), "http://localhost:8086/SayHello"); if (obj == null) { System.Console.WriteLine( "Could not locate HTTP server"); } MySponsor sponsor = new MySponsor(); sponsor.RenewalTime = TimeSpan.FromMinutes(2); sponsor.Register(obj); ILease lease = (ILease)obj.GetLifetimeService(); if (lease != null) { Console.WriteLine("Lease Configuration:"); Console.WriteLine("InitialLeaseTime: " + lease.InitialLeaseTime); Console.WriteLine("RenewOnCallTime: " + lease.RenewOnCallTime); Console.WriteLine("SponsorshipTimeout: " + lease.SponsorshipTimeout); Console.WriteLine(lease.CurrentLeaseTime); } } } public class MySponsor:ClientSponsor,ISponsor { TimeSpan ISponsor.Renewal(ILease lease) { Console.WriteLine("Renewal called"); return this.RenewalTime; } } }
五.总结
经过租约来管理远程对象的生存期能够做为引用计数的一种替代方法,由于当网络链接的性能不可靠时,引用计数会显得复杂和低效。尽管有人会坚持认为远程对象的生存期比所需的时间要长,但与引用计数和链接客户相比,租约下降了网络的繁忙程度,将会成为一种很是受欢迎的解决方案。
附录:一个完整的用程序代码配置租约生存期的例子
Server.cs
using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Channels.Http; namespace RemotingSamples { public class Server { public static int Main(string [] args) { TcpChannel chan1 = new TcpChannel(8085); HttpChannel chan2 = new HttpChannel(8086); ChannelServices.RegisterChannel(chan1); ChannelServices.RegisterChannel(chan2); //服务器端激活。 RemotingConfiguration.RegisterWellKnownServiceType ( &bbsp; typeof(HelloServer), "SayHello", WellKnownObjectMode.Singleton ); System.Console.WriteLine("Press Enter key to exit"); System.Console.ReadLine(); return 0; } } }
HelloWord.cs
using System; using System.Collections.Generic; using System.Text; using System.Runtime.Remoting.Lifetime; namespace RemotingSamples { public class HelloServer : MarshalByRefObject { public HelloServer() { Console.WriteLine("HelloServer activated"); } public String HelloMethod(String name) { Console.WriteLine( "Server Hello.HelloMethod : {0}", name); return "Hi there " + name; } // Overrides the lease settings for this object. public override object InitializeLifetimeService() { ILease lease = (ILease)base.InitializeLifetimeService(); // Normally, the initial lease time would be much longer. // It is shortened here for demonstration purposes. if (lease.CurrentState == LeaseState.Initial) { lease.InitialLeaseTime = TimeSpan.FromSeconds(3); lease.SponsorshipTimeout = TimeSpan.FromSeconds(10); lease.RenewOnCallTime = TimeSpan.FromSeconds(2); } return lease; } } }
Client.cs
using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Remoting.Activation; using System.Runtime.Remoting.Lifetime; using System.IO; namespace RemotingSamples { public class Client { public static void Main(string[] args) { //使用TCP通道获得远程对象 ChannelServices.RegisterChannel(new HttpChannel()); HelloServer obj = (HelloServer)Activator.GetObject( typeof(RemotingSamples.HelloServer), "http://localhost:8086/SayHello"); if (obj == null) { System.Console.WriteLine( "Could not locate HTTP server"); } ILease lease = (ILease)obj.GetLifetimeService(); if (lease != null) { Console.WriteLine("Lease Configuration:"); Console.WriteLine("InitialLeaseTime: " + lease.InitialLeaseTime); Console.WriteLine("RenewOnCallTime: " + lease.RenewOnCallTime); Console.WriteLine("SponsorshipTimeout: " + lease.SponsorshipTimeout); Console.WriteLine(lease.CurrentLeaseTime); } } } }
出处:http://www.cnblogs.com/Terrylee/archive/2005/11/28/285809.html