HuangPeng

暂无签名

  • 博客访问: 217124
  • 博文数量: 286
  • 用 户 组: 普通用户
  • 注册时间: 1970-01-01 08:00
个人简介

鏆傛棤浠嬬粛

ITPUB论坛APP

ITPUB论坛APP



APP发帖 享双倍积分

文章分类

全部博文(286)

文章存档

2007年(195)

2006年(68)

2004年(23)

我的朋友
微信关注

IT168企业级官微



微信号:IT168qiye



系统架构师大会



微信号:SACC2013

分类: 微软技术

HTTP 通道

HTTP 通道使用 SOAP 协议与远程对象传输消息。所有的消息流过 SOAP 格式化程序时都被转换为 XML 格式且被序列化,所需的 SOAP 头也会被添加到该流中。您也可以指定能够生成二进制数据流的二进制格式化程序。然后,数据流会使用 HTTP 协议传输到目标 URI。

TCP 通道

TCP 通道使用二进制格式化程序将所有的消息序列化为二进制流,并使用 TCP 协议将其传输到目标 URI。

激活

远程处理框架支持远程对象的服务器激活和客户端激活。不需要远程对象在方法调用之间维护任何状态时,一般使用服务器激活。服务器激活也适用于多个客户端调用方法位于同一对象实例上、且对象在函数调用之间维持状态的情况。另一方面,客户端激活对象从客户端实例化,并且客户端通过使用基于租用的专用系统来管理远程对象的生存期。

在可以接受客户端的访问之前,所有的远程对象都必须用远程处理框架注册。对象注册一般由宿主应用程序来完成。宿主应用程序将启动,使用 ChannelServices 注册一个或多个通道,使用 RemotingServices 注册一个或多个远程对象,然后等待被终止。请注意,已注册的通道和对象只有在用来注册它们的进程活动时才可以使用。如果退出了该进程,则会自动从远程处理服务中删除它注册的所有通道和对象。在框架中注册远程对象时,需要以下四项信息:

  1. 包含类的程序集名称。
  2. 远程对象的类型名称。
  3. 客户端定位对象时将使用的对象 URI。
  4. 服务器激活所需的对象模式。该模式可以是 SingleCall,也可以是 Singleton

远程对象可以通过下列两种方式注册:调用 RegisterWellKnownType,将上述信息作为参数传递;或将上述信息存储在配置文件中,然后调用 ConfigureRemoting 并将该配置文件的名称作为参数传递。以上两种方法执行的功能相同,因此您可以使用它们中的任意一种来注册远程对象。当然,后一种方法更方便些,因为无需重新编译宿主应用程序即可改变配置文件的内容。以下代码片断显示了如何将 HelloService 类注册为 SingleCall 远程对象。

RemotingServices.RegisterWellKnownType(  "server",  "Samples.HelloServer",  "SayHello",   WellKnownObjectMode.SingleCall);

其中,“server”是程序集的名称,HelloServer 是类的名称,SayHello 是对象 URI。

注册了远程对象后,框架将为该对象创建一个对象引用,然后从程序集中提取与该对象相关的必要元数据。随后,这一信息将与 URI 和程序集名称一起存储在对象引用中(该对象引用将被写入一个用于跟踪已注册远程对象的远程处理框架表中)。请注意,除了在客户端试图调用对象上的某个方法或从客户端激活对象时以外,注册进程不会实例化远程对象自身。

现在,任何知道该对象 URI 的客户端都可以使用 ChannelServices 注册通道,并调用 newGetObjectCreateInstance 激活对象,从而获得该对象的一个代理。以下代码片断显示了该操作的示例:

      ChannelServices.RegisterChannel(new TCPChannel);      HelloServer obj = (HelloServer)Activator.GetObject(         typeof(Samples.HelloServer), "tcp://localhost:8085/SayHello");

其中,“tcp://localhost:8085/SayHello”表示我们希望在端口 8085 上使用 TCP 协议连接到位于 SayHello 终结点的远程对象。在编译该客户端代码时,编译器明显会要求关于 HelloServer 类的类型信息。该信息可以通过以下方式之一来提供:

  • 提供对 HelloService 类所在程序集的引用。
  • 将远程对象拆分为实现和接口类,并在编译客户端时引用这些接口。
  • 使用 SOAPSUDS 工具直接从终结点提取所需的元数据。此工具将连接至所提供的终结点,提取元数据,然后生成可用于编译客户端的程序集或源代码。

GetObjectnew 可用于服务器激活对象。请注意,使用这两个调用时不会实例化对象,实际上不会生成任何网络调用。框架从元数据获得了创建代理所需的足够信息,但并未连接到远程对象上。只有在客户端调用代理上的某个方法时才会建立网络连接。当调用抵达服务器时,框架将从消息中提取 URI,检查远程处理框架表以便定位与 URI 匹配的对象引用,然后在必要时将对象实例化,并将方法调用转发至对象。如果将对象注册为 SingleCall,则完成方法调用后该对象会取消。每次调用一个方法时,都会创建一个新的实例。GetObjectnew 之间的唯一差别在于,前者允许指定 URL 作为参数,而后者从配置中获得 URL。

CreateInstancenew 可用于客户端激活对象。两者都允许使用带参数的构造函数来实例化对象。客户端激活对象的生存期由远程处理框架提供的租用服务控制。对象租用的内容在下一节中说明。

对象的租用生存期

每个应用程序域都包含一个用于管理其租用情况的租用管理器。所有的租用都会被定期检查,以确定租用是否已过期。如果租用过期,则会调用该租用的一个或多个发起者,使它们有机会更新租用。如果所有的发起者都不准备更新租用,则租用管理器会删除该租用并将该对象作为垃圾回收。租用管理器按照剩余租用时间的顺序维护租用列表。剩余时间最短的租用排在列表的顶端。

租用可以实现 ILease 接口并存储一个属性集合,用于确定更新的策略和方法。您也可以使用调用来更新租用。每次调用远程对象上的方法时,租用时间都会设置为目前 LeaseTime 最大值加上 RenewOnCallTimeLeaseTime 即将过期时,发起者会被要求更新租用。因为我们有时会遇上网络不稳定,所以可能会找不到租用发起者。为了确保不在服务器上留下无效对象,每个租用都带有一个 SponsorshipTimeout。该值指定了租用终止之前等待租用发起者回复的时间长度。如果 SponsershipTimeout 为零,CurrentLeaseTime 会被用于确定租用的过期时间。如果 CurrentLeaseTime 的值为零,则租用不会过期。配置或 API 可用于替代 InitialLeaseTimeSponsorshipTimeoutRenewOnCallTime 的默认值。

租用管理器维护着一个按发起时间从大到小存储的发起者列表(它们实现 ISponsor 接口)。需要调用发起者以更新租用时间时,租用管理器会从列表的顶部开始向一个或多个发起者要求更新租用时间。列表顶部的发起者表示其以前请求的租用更新时间最长。如果发起者没有在 SponsorshipTimeOut 时间段内响应,则它会被从列表中删除。通过调用 GetLifetimeService 并将对象租用作为参数,即可以获得该对象租用。该调用是 RemotingServices 类的一个静态方法。如果对象在应用程序域内部,则该调用的参数是对象的本地引用,且返回的租用也是该租用的本地引用。如果对象是远程的,则代理会作为一个参数传递,且返回给调用方的是租用的透明代理。

对象能够提供自己的租用并控制自己的生存期。它们通过替代 MarshalByRefObject 上的 InitializeLifetimeService 方法来完成该操作,如下所示:

public class Foo : MarshalByRefObject {  public override Object InitializeLifetimeService()  {    ILease lease = (ILease)base.InitializeLifetimeService();    if (lease.CurrentState == LeaseState.Initial)  {      lease.InitialLeaseTime = TimeSpan.FromMinutes(1);      lease.SponsorshipTimeout = TimeSpan.FromMinutes(2);      lease.RenewOnCallTime = TimeSpan.FromSeconds(2);    }    return lease;  }}

只有当租用处于初始状态时,才可以更改租用属性。InitializeLifetimeService 的实现通常调用基类的相应方法,来检索远程对象的现有租用。如果在此之前从未对该对象封送过,则返回的租用会处于其初始状态且可以设置租用属性。一旦封送了对象,则租用会从初始状态变为激活状态,并忽略任何初始化租用属性的尝试(但有一种情况例外)。激活远程对象时将调用 InitializeLifetimeService。通过激活调用可以提供一个租用发起者的列表,而且当租用处于激活状态时,可以随时将其他发起者添加到列表中。

可以下列方式延长租用时间:

  • 客户端可以调用 Lease 类上的 Renew 方法。
  • 租用可以向某个发起者请求 Renewal
  • 当客户端调用对象上的某个方法时,RenewOnCall 值会自动更新租用。

一旦租用过期,其内部状态会由 Active 变为 Expired,且不再对发起者进行任何调用,对象也会被作为垃圾回收。一般情况下,如果发起者分散在 Web 上或位于某个防火墙的后面,远程对象回叫发起者时会遇到困难。因此,发起者不必与客户端处于同一位置,只要远程对象能够访问得到,它可以为网络上的任意位置。

通过租用来管理远程对象的生存期可以作为引用计数的一种替代方法,因为当网络连接的性能不可靠时,引用计数会显得复杂和低效。尽管有人会坚持认为远程对象的生存期比所需的时间要长,但与引用计数和连接客户相比,租用降低了网络的繁忙程度,将会成为一种非常受欢迎的解决方案。

总结

要提供完美的、能够满足大多数商务应用需求的远程处理框架,即使能够做到,也必然会非常困难。Microsoft 提供了能够根据需要进行扩展和自定义的框架,在正确的方向上迈出了关键的一步。

附录 A:使用 TCP 通道进行远程处理的示例

此附录显示了如何编写简单的“Hello World”远程应用程序。客户端将一个字符串传递到远程对象上,该远程对象将单词“Hi There”附加到字符串上,并将结果返回到客户端。

将此文件保存为 server.cs。此处为服务器的代码:

using System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels.TCP;namespace RemotingSamples {  public class HelloServer : IHello {    public static int Main(string [] args) {      TCPChannel chan = new TCPChannel(8085);      ChannelServices.RegisterChannel(chan);      RemotingServices.RegisterWellKnownType(        "server", "RemotingSamples.HelloServer", "SayHello", 
WellKnownObjectMode.SingleCall); System.Console.WriteLine("请按 键退出..."); System.Console.ReadLine(); return 0; } public HelloServer() { Console.WriteLine("HelloServer 已激活"); } ~HelloServer() { Console.WriteLine("对象已清除"); } public ForwardMe HelloMethod(ForwardMe obj) { Console.WriteLine("Hello.HelloMethod : {0}", name); return "Hi there " + name; } }}

将此代码保存为 client.cs

using System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels.TCP;namespace RemotingSamples {  public class Client  {    public static int Main(string [] args)    {      TCPChannel chan = new TCPChannel();      ChannelServices.RegisterChannel(chan);      ForwardMe param = new ForwardMe();      HelloServer obj = (HelloServer)Activator.GetObject(        typeof(RemotingSamples.HelloServer), "tcp://localhost:8085/SayHello");      if (obj == null) System.Console.WriteLine("无法定位服务器");      else {        Console.WriteLine("值为 " + param.getValue());        ForwardMe after = obj.HelloMethod(param);        Console.WriteLine("呼叫后的值为 " + after.getValue());      }      return 0;    }  }}

下面是 makefile

all: server.exe client.exe share.dllshare.dll: share.cs   csc /debug+ /target:library /out:share.dll share.csserver.exe: server.cs   csc /debug+ /r:share.dll /r:System.Runtime.Remoting.dll server.csclient.exe: client.cs server.exe   csc /debug+ /r:share.dll /r:server.exe /r:System.Runtime.Remoting.dll client.csclean:   @del server.exe client.exe *.pdb *~ *.*~

附录 B:使用 HTTP 通道进行远程处理的示例

将此文件保存为 server.cs。此处为服务器的代码:

using System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels.HTTP;namespace RemotingSamples {  public class HelloServer : IHello {    public static int Main(string [] args) {      HTTPChannel chan = new HTTPChannel(8085);      ChannelServices.RegisterChannel(chan);      RemotingServices.RegisterWellKnownType(        "server", "RemotingSamples.HelloServer", "SayHello", 
WellKnownObjectMode.SingleCall); System.Console.WriteLine("请按 键退出..."); System.Console.ReadLine(); return 0; } public HelloServer() { Console.WriteLine("HelloServer 已激活"); } ~HelloServer() { Console.WriteLine("对象已清除"); } public ForwardMe HelloMethod(ForwardMe obj) { Console.WriteLine("Hello.HelloMethod : {0}", name); return "Hi there " + name; } }}

将此代码保存为 client.cs

using System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels.HTTP;namespace RemotingSamples {  public class Client  {    public static int Main(string [] args)    {      HTTPChannel chan = new HTTPChannel();      ChannelServices.RegisterChannel(chan);      ForwardMe param = new ForwardMe();      HelloServer obj = (HelloServer)Activator.GetObject(        typeof(RemotingSamples.HelloServer), "http://localhost:8085/SayHello");      if (obj == null) System.Console.WriteLine("无法定位服务器");      else {        Console.WriteLine("值为 " + param.getValue());        ForwardMe after = obj.HelloMethod(param);        Console.WriteLine("呼叫后的值为 " + after.getValue());      }      return 0;    }  }}

下面是 makefile

all: server.exe client.exe share.dllshare.dll: share.cs   csc /debug+ /target:library /out:share.dll share.csserver.exe: server.cs   csc /debug+ /r:share.dll /r:System.Runtime.Remoting.dll server.csclient.exe: client.cs server.exe   csc /debug+ /r:share.dll /r:server.exe /r:System.Runtime.Remoting.dll client.csclean:   @del server.exe client.exe *.pdb *~ *.*~
[ 本日:
[@more@]
阅读(544) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册