使用.NET Remoting開發分佈式應用——基於租約的生存期

一.概述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 最大值加上 RenewOnCallTimeLeaseTime 即將過時時,發起者會被要求更新租約。由於咱們有時會趕上網絡不穩定,因此可能會找不到租約發起者。爲了確保不在服務器上留下無效對象,每一個租約都帶有一個 SponsorshipTimeout。該值指定了租約終止以前等待租約發起者回復的時間長度。若是 SponsershipTimeout 爲零,CurrentLeaseTime 會被用於肯定租約的過時時間。若是 CurrentLeaseTime 的值爲零,則租約不會過時。配置或 API 可用於替代 InitialLeaseTimeSponsorshipTimeout 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。經過激活調用能夠提供一個租約發起者的列表,並且當租約處於激活狀態時,能夠隨時將其餘發起者添加到列表中。

能夠下列方式延長租約時間:

  • 客戶端能夠調用 Lease 類上的 Renew 方法。
  • 租約能夠向某個發起者請求 Renewal
  • 當客戶端調用對象上的某個方法時,RenewOnCall 值會自動更新租約。

一旦租約過時,其內部狀態會由 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

相關文章
相關標籤/搜索