Microsoft .Net Remoting系列专题之二

一、远程对象的激活

在Remoting中有三种激活方式,一般的实现是通过RemotingServices类的静态方法来完成。工作过程事实上是将该远程对象注册到通道中。由于Remoting没有提供与之对应的Unregister方法来注销远程对象,所以如果需要注册/注销指定对象,微软推荐使用Marshal(一般译为编组)和Disconnect配对使用。在《Net Remoting基础篇》中我已经谈到:Marshal()方法是将MarshalByRefObject类对象转化为ObjRef类对象,这个对象是存储生成代理以与远程对象通讯所需的所有相关信息。这样就可以将该实例序列化以便在应用程序域之间以及通过网络进行传输,客户端就可以调用了。而Disconnect()方法则将具体的实例对象从通道中断开。

根据上述说明,Marshal()方法对远程对象以引用方式进行编组(Marshal-by-Reference,MBR),并将对象的代理信息放到通道中。客户端可以通过Activator.GetObject()来获取。如果用户要注销该对象,则通过调用Disconnect()方法。那么这种方式对于编组的远程对象是否存在生命周期的管理呢?这就是本文所要描述的问题。

二、生命周期

在CLR中,框架提供了GC(垃圾回收器)来管理内存中对象的生命周期。同样的,.Net Remoting使用了一种分布式垃圾回收,基于租用的形式来管理远程对象的生命周期。

早期的DCOM对于对象生命周期的管理是通过ping和引用计数来确定对象何时应当作为垃圾回收。然而ping引起的网络流量对分布式应用程序的性能是一种痛苦的负担,它大大地影响了分布式处理的整体性能。.Net Remoting在每个应用程序域中都引入一个租用管理器,为每个服务器端的SingleTon,或每个客户端激活的远程对象保存着对租用对象的引用。(说明:对于服务器端激活的SingleCall方式,由于它是无状态的,对于每个激活的远程对象,都由CLR的GC来自动回收,因此对于SingleCall模式激活的远程对象,不存在生命周期的管理。)

1、租用

租用是个封装了TimeSpan值的对象,用以管理远程对象的生存期。在.Net Remoting中提供了定义租用功能的ILease接口。当Remoting通过SingleTon模式或客户端激活模式来激活远程对象时,租用对象调用从System.MarshalByRefObject继承的InitializeLifetimeService方法,向对象请求租用。

ILease接口定义了有关生命周期的属性,均为TimeSpan值。如下: InitialLeaseTime:初始化有效时间,默认值为300秒,如果为0,表示永不过期; RenewOnCallTime:调用远程对象一个方法时的租用更新时间,默认值为120秒; SponsorshipTimeout:超时值,通知Sponsor(发起人)租用过期后,Remoting会等待的时间,默认值为120秒; CurrentLeaseTime:当前租用时间,首次获得租用时,为InitializeLeaseTime的值。

Remoting的远程对象因为继承了MarshalByRefObject,因此默认继承了InitializeLifetimeService方法,那么租用的相关属性为默认值。如果要改变这些设置,可以在远程对象中重写该方法。例如:  public override object InitializeLifetimeService()  {   ILease lease = (ILease)base.InitializeLifetimeService();   if (lease.CurrentState == LeaseState.Initial)   {    lease.InitialLeaseTime = TimeSpan.FromMinutes(1);    lease.RenewOnCallTime = TimeSpan.FromSeconds(20);   }   return lease;    }

也可以忽略该方法,将对象的租用周期改变为无限:  public override object InitializeLifetimeService()  {   return null;  }

2、租用管理器

如果是前面所说的租用主要是应用在每个具体的远程对象上,那么租用管理器是服务器端专门用来管理远程对象生命周期的管理器,它维持着一个System.Hashtable成员,将租用映射为System.DateTime实例表示每个租用何时应过期。Remoting采用轮询的方式以一定的时间唤醒租用管理器,检查每个租用是否过期。默认为每10秒钟唤醒一次。轮询的间隔可以配置,如将轮询间隔设置为5分钟:LifetimeService.LeaseManagerPollTime = System.TimeSpan.FromMinutes(5);

我们还可以在租用管理器中设置远程对象租用的属性,如改变远程对象的初始有效时间为永久有效: LifetimeServices.LeaseTime = TimeSpan.Zero;

我们也可以通过配置文件来设置生命周期,如: <configuration>  <system.runtime.remoting>   <application name = "SimpleServer">    <lifetime leaseTime = "0" sponsorshipTimeOut = "1M" renewOnCallTime = "1M" pollTime = "30S"/>          </application>  </system.runtime.remoting> </configuration>

注:配置文件中的pollTime即为上面所说的租用管理器的轮询间隔时间LeaseManagerPollTime。

租用管理器对于生命周期的设置是针对服务器上所有的远程对象。当我们通过配置文件或租用管理器设置租用的属性时,所有远程对象的生命周期都遵循该设置,除非我们对于指定的远程对象通过重写InitializeLifetimeService方法,改变了相关配置。也就是说,远程对象的租用配置优先级高于服务器端配置。

3、发起人(Sponsor)

发起人是针对客户端而言的。远程对象就是发起人要租用的对象,发起人可以与服务器端签订租约,约定租用时间。一旦到期后,发起人还可以续租,就像现实生活中租方的契约,房东、租房者之间的关系一样。

在.Net Framework中的System.Runtime.Remoting.Lifetime命名空间中定义了ClientSponsor类,该类继承了System.MarshalByRefObject,并实现了ISponsor接口。ClientSponsor类的属性和方法,可以参考MSDN。

客户端要使用发起人机制,必须创建ClientSponsor类的一个实例。然后调用相关方法如Register()或Renewal()方法来注册远程对象或延长生命周期。如: RemotingObject obj = new RemotingObject(); ClientSponsor sponsor = new ClientSponsor(); sponsor.RenewalTime = TimeSpan.FromMinutes(2); sponsor.Register(obj);

续租时间也可以在ClientSponsor的构造函数中直接设置,如: ClientSponsor sponsor = new ClientSponsor(TimeSpan.FromMinutes(2)); sponsor.Register(obj);

我们也可以自己编写Sponsor来管理发起人机制,这个类必须继承ClientSponsor并实现ISponsor接口。

三、跟踪服务

如前所述,我们要判断通过Marshal编组远程对象是否存在生命周期的管理。在Remoting中,可以通过跟踪服务程序来监视MBR对象的编组进程。

我们可以创建一个简单的跟踪处理程序,该程序实现接口ITrackingHandler。接口ITrackingHandler定义了3个方法,MarshalObject、UnmarshalObject和DisconnectedObject。当远程对象被编组、解组和断开连接时,就会调用相应的方法。下面是该跟踪处理类的代码:public class MyTracking:ITrackingHandler {  public MyTracking()  {   //   // TODO: 在此处添加构造函数逻辑   //  }

public void MarshaledObject(object obj,ObjRef or)  {   Console.WriteLine();   Console.WriteLine("对象" + obj.Tostring() + " is marshaled at " + DateTime.Now.ToShortTimeString());  }

public void UnmarshaledObject(object obj,ObjRef or)  {   Console.WriteLine();   Console.WriteLine("对象" + obj.Tostring() + " is unmarshaled at " + DateTime.Now.ToShortTimeString());  }

public void DisconnectedObject(object obj)  {   Console.WriteLine(obj.ToString() + " is disconnected at " + DateTime.Now.ToShortTimeString());  } }

然后再服务器端创建该跟踪处理类的实例,并注册跟踪服务: TrackingServices.RegisterTrackingHandler(new MyTracking());

四、测试

1、建立两个远程对象,并重写InitializeLifetimeService方法:

对象一:AppService1 初始生命周期:1分钟

public class AppService1:MarshalByRefObject  {   public void PrintString(string contents)   {    Console.WriteLine(contents);      }

public override object InitializeLifetimeService()   {    ILease lease = (ILease)base.InitializeLifetimeService();    if (lease.CurrentState == LeaseState.Initial)    {     lease.InitialLeaseTime = TimeSpan.FromMinutes(1);     lease.RenewOnCallTime = TimeSpan.FromSeconds(20);    }    return lease;       }  }

对象二:AppService2 初始生命周期:3分钟

public class AppService2:MarshalByRefObject  {   public void PrintString(string contents)   {    Console.WriteLine(contents);      }

public override object InitializeLifetimeService()   {    ILease lease = (ILease)base.InitializeLifetimeService();    if (lease.CurrentState == LeaseState.Initial)    {     lease.InitialLeaseTime = TimeSpan.FromMinutes(3);     lease.RenewOnCallTime = TimeSpan.FromSeconds(40);    }    return lease;       }  }

为简便起见,两个对象的方法都一样。

2、服务器端

(1) 首先建立如上的监控处理类;

(2) 注册通道: TcpChannel channel = new TcpChannel(8080); ChannelServices.RegisterChannel(channel);

(3) 设置租用管理器的初始租用时间为无限: LifetimeServices.LeaseTime = TimeSpan.Zero;

(4) 创建该跟踪处理类的实例,并注册跟踪服务: TrackingServices.RegisterTrackingHandler(new MyTracking());

(5) 编组两个远程对象: ServerAS.AppService1 service1 = new ServerAS1.AppService1(); ObjRef objRef1 = RemotingServices.Marshal((MarshalByRefObject)service1,"AppService1");

ServerAS.AppService2 service2 = new ServerAS1.AppService2(); ObjRef objRef2 = RemotingServices.Marshal((MarshalByRefObject)service2,"AppService2");

(6) 使服务器端保持运行: Console.WriteLine("Remoting服务启动,按退出...");  Console.ReadLine();

3、客户端

通过Activator.GetObject()获得两个远程对象,并调用其方法PrintString。代码略。

4、运行测试:

运行服务器端和客户端,由于监控程序将监视远程对象的编组进程,因此在运行开始,就会显示远程对象已经被Marshal:

然后再客户端调用这两个远程对象的PrintString方法,服务器端接受字符串:

一分钟后,远程对象一自动被Disconnect:

此时客户端如要调用远程对象一,会抛出RemotingException异常;

又一分钟后,远程对象二被Disconnect了:

align="center">

用户还可以根据这个代码测试RenewOnCallTime的时间是否正确。也即是说,在对象还未被Disconnect时,调用对象,则从调用对象的这一刻起,其生命周期不再是原来设定的初始有效时间值(InitialLeaseTime),而是租用更新时间值(RenewOnCallTime)。另外,如果这两个远程对象没有重写InitializeLifetimeService方法,则生命周期应为租用管理器所设定的值,为永久有效(设置为0)。那么这两个对象不会被自动Disconnect,除非我们显式指定关闭它的连接。当然,如果我们显式关闭连接,跟踪程序仍然会监视到它的变化,然后显示出来。

五、结论

通过我们的测试,其实结论已经很明显了。通过Marshal编组的对象要受到租用的生命周期所控制。注意对象被Disconnect,并不是指这个对象被GC回收,而是指这个对象保存在通道的相关代理信息被断开了,而对象本身仍然在服务器端存在。

所以我们通过Remoting提供服务,应根据实际情况指定远程对象的生命周期,如果不指定,则为Remoting默认的设定。要让所有的远程对象永久有效,可以通过配置文件或租用管理器将初始有效时间设为0。

转自:http://www.cnblogs.com/wayfarer/archive/2004/08/05/30437.html

Microsoft .Net Remoting系列专题之二的更多相关文章

  1. Microsoft .Net Remoting系列专题之一:.Net Remoting基础篇

    Microsoft .Net Remoting系列专题之一 一.Remoting基础 什么是Remoting,简而言之,我们可以将其看作是一种分布式处理方式.从微软的产品角度来看,可以说Remotin ...

  2. Microsoft .Net Remoting系列专题之三:Remoting事件处理全接触

    前言:在Remoting中处理事件其实并不复杂,但其中有些技巧需要你去挖掘出来.正是这些技巧,仿佛森严的壁垒,让许多人望而生畏,或者是不知所谓,最后放弃了事件在Remoting的使用.关于这个主题,在 ...

  3. TensorFlow系列专题(二):机器学习基础

    欢迎大家关注我们的网站和系列教程:http://www.tensorflownews.com/ ,学习更多的机器学习.深度学习的知识! 目录: 数据预处理 归一化 标准化 离散化 二值化 哑编码 特征 ...

  4. [.NET领域驱动设计实战系列]专题十一:.NET 领域驱动设计实战系列总结

    一.引用 其实在去年本人已经看过很多关于领域驱动设计的书籍了,包括Microsoft .NET企业级应用框架设计.领域驱动设计C# 2008实现.领域驱动设计:软件核心复杂性应对之道.实现领域驱动设计 ...

  5. [.NET领域驱动设计实战系列]专题十:DDD扩展内容:全面剖析CQRS模式实现

    一.引言 前面介绍的所有专题都是基于经典的领域驱动实现的,然而,领域驱动除了经典的实现外,还可以基于CQRS模式来进行实现.本专题将全面剖析如何基于CQRS模式(Command Query Respo ...

  6. 【转】Microsoft .Net Remoting 之.Net Remoting基础篇

    .Net Remoting基础篇 一.Remoting基础 什么是Remoting,简而言之,我们可以将其看作是一种分布式处理方式.从微软的产品角度来看,可以说Remoting就是DCOM的一种升级, ...

  7. [C# 基础知识梳理系列]专题六:泛型基础篇——为什么引入泛型

    引言: 前面专题主要介绍了C#1中的2个核心特性——委托和事件,然而在C# 2.0中又引入一个很重要的特性,它就是泛型,大家在平常的操作中肯定会经常碰到并使用它,如果你对于它的一些相关特性还不是很了解 ...

  8. Microsoft .Net Remoting

    什么是Remoting,简而言之,我们可以将其看作是一种分布式处理方式.从微软的产品角度来看,可以说Remoting就是DCOM的一种升级,它改善了很多功能,并极好的融合到.Net平台下.Micros ...

  9. CRL快速开发框架系列教程十二(MongoDB支持)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

随机推荐

  1. 会话跟踪Cookie与Session

    会话跟踪用来跟踪用户的整个会话,常用的是Session和Cookie.Cookie通过客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份. 1.Cookie  1.1概念及使 ...

  2. Spring AOP With AspectJ

    一.AOP和拦截器 某些情况下,AOP和拦截器包括Filter能够实现同样的功能,一般都是请求即controller层的操作,这三个执行顺序为Filter>Interceptor>AOP, ...

  3. FaceRank-项目上了 GitHub Python Trending

    FaceRank-人脸打分基于 TensorFlow 的 CNN 模型 GitHub Python Trending 第一次上 trending ,虽然只是分类榜. https://github.co ...

  4. Python爬虫番外篇之关于登录

    常见的登录方式有以下两种: 查看登录页面,csrf,cookie;授权:cookie 直接发送post请求,获取cookie 上面只是简单的描述,下面是详细的针对两种登录方式的时候爬虫的处理方法 第一 ...

  5. [补档][Hnoi2013]游走

    [Hnoi2013]游走 题目 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一 ...

  6. mysql中varchar和char区别(思维导图整理)

    由于mysql一直是我的弱项(其实各方面我都是很弱的),所以最近在看msyql,正好看到varchar和char区别,所以整理一下,便于以后遗忘. 0.0图片已经说明一切,但是系统说我字数不够,我真能 ...

  7. 【head first python】2.共享你的代码 函数模块

    #coding:utf-8 #注释代码! #添加两个注释,一个描述模块,一个描述函数 '''这是nester.py模块,提供了一个名为print_lol()的函数, 这个函数的作用是打印列表,其中可能 ...

  8. sublime text3 配置使用

    前言 sublime text3 是一款优秀的代码编辑器,很多好用的功能让我成为其忠实用户. 流程 1.安装完成后首先配置package control,把下面的代码复制到sublime 的控制台: ...

  9. shell编程之数组

    bash 编程只支持一维数组,不支持多维,类似c语言,数组下标从0开始,下标可以是整数,也可以是表达式 数组的定义 在shell中用括号来表示数组,中间用空格来隔开 主要有两种种定义形式: arr=( ...

  10. mysql为什么范围查询(>,<,between,%like,like%)之后的索引无效

    因为使用了范围索引,所以会使用满足范围的所有的值,也就是说存储引擎在这个时候会提取出满足之后条件的所有值,并遍历获取满足之后条件的值. http://www.itpub.net/thread-1901 ...