使用.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

 1using System;
 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

 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            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开发分布式应用——基于租约的生存期(转载)相关推荐

  1. 初识用.NET Remoting来开发分布式应用 (转载)

    初识用.NET Remoting来开发分布式应用 一..NET Remoting简介: .NET Remoting从某种意义上讲是DCOM的替代品.ASP.NET Web服务十分有用,但是这项技术在企 ...

  2. 跟我一起学WCF(2)——利用.NET Remoting技术开发分布式应用

    一.引言 上一篇博文分享了消息队列(MSMQ)技术来实现分布式应用,在这篇博文继续分享下.NET平台下另一种分布式技术--.NET Remoting. 二..NET Remoting 介绍 2.1 . ...

  3. 利用Web Services开发分布式应用

    一.引言 在前面文章中分别介绍了MSMQ和.NET Remoting技术,今天继续分享.NET 平台下另一种分布式技术--Web Services 二.Web Services 详细介绍 2.1 We ...

  4. java 写一个商店_Java Web开发之基于Session的购物商店实现方法

    本文实例讲述了Java Web开发之基于Session的购物商店实现方法.分享给大家供大家参考,具体如下: package cn.com.shopping; import java.io.IOExce ...

  5. 认识Web前端、Web后端、桌面app和移动app新开发模式 - 基于Node.js环境和VS Code工具...

    认识Web.桌面和移动app新开发模式 - 基于Node.js环境和VS Code工具 一.开发环境的搭建(基于win10) 1.安装node.js和npm 到node.js官网下载安装包(包含npm ...

  6. 构建一个基本的前端自动化开发环境 —— 基于 Gulp 的前端集成解决方案(四)

    构建一个基本的前端自动化开发环境 -- 基于 Gulp 的前端集成解决方案(四) 参考文章: (1)构建一个基本的前端自动化开发环境 -- 基于 Gulp 的前端集成解决方案(四) (2)https: ...

  7. 学习《Flask Web开发:基于Python的Web应用开发实战》分享

    学习<Flask Web开发:基于Python的Web应用开发实战>分享一直在说学习Python,对同事,对朋友,都说我正在学习Python,这无形给自己一定的压力,促使自己要去学习,进步 ...

  8. 使用php开发,基于swoole扩展开发的工具 swoole-crontab

    2019独角兽企业重金招聘Python工程师标准>>> 使用php开发,基于swoole扩展开发的工具 swoole-crontab https://www.oschina.net/ ...

  9. java osgi web开发_基于 OSGi 和 Spring 开发 Web 应用

    开发一个简单的OSGi Web应用实例 一个简单的Web应用 我们写一个简单的 Web 应用 compute.html :计算两个数字的和或乘积.如下图所示: 图1.一个简单例子 一个简单例子.bmp ...

最新文章

  1. oracle中创建函数行变列,oracle decode 函数实现行转列
  2. Java10-day01【继承(变量-构造方法访问-成员方法 访问)、super内存图、方法重写、继承、包、import、权限修饰符、final、static】
  3. 【maven】pom常用配置
  4. idea配置连接oracle数据库的pom文件中添加什么?
  5. 2020年平均工资出炉!这个行业最高
  6. 企业网络之间资源互访
  7. 122 - Trees on the level(模拟内存池解法)
  8. linux方面的杂谈
  9. input表单的type属性详解,不同type不同属性之间区别
  10. Python爬虫入门教程 42-100 爬取儿歌多多APP数据-手机APP爬虫部分
  11. c语言指针详解 PPT,最全C语言指针详解.ppt
  12. mysql中tab键作用_MySQL小技巧-mysql命令 tab键数据表名、字段名补全功能
  13. monetDb列式存储架构分析
  14. 大一新生的第一篇博客
  15. 【零散知识】核密度估计(Kernel Density Estimation)
  16. 精选100个Python实战项目案例,送给零基础小白的你
  17. 由点及面,一叶知秋------集合大家庭
  18. 如何查询设备序列号?其实一条命令搞定!
  19. 【英语阅读】纽约时报 | 你妈注定让你抓狂
  20. MYSQL查询员工信息练习

热门文章

  1. c++中的map容器
  2. c++中空指针访问成员函数
  3. c++仿函数 functor
  4. 自定义类型: 结构体,枚举,联合
  5. 关于NOR FLASH地址左右移的问题
  6. Redis高级项目实战!北京java编程入门培训
  7. 做了6年的Java,docker端口映射无法外部访问
  8. 使用Docker快速搭建Tensorflow开发环境
  9. UML 中extend和include的区别
  10. 程序员福利各大平台免费接口,非常适用