简介
远程方法调用发展到现在已经有以下几种框架实现DCE/RPCCORBADCOMMTS/COM+Java RMIJava EJBWeb Services/SOAP/XMLRPCNET Remoting本文主要介绍了NET远程方法调用的原理实现以及与微软COM/DCOM实现的异同点
框架
Microsoft NET Remoting 提供了一种允许对象通过应用程序域与另一对象进行交互的框架众所周知Web服务仅仅提供了一种简单的容易理解的方法来实现跨平台跨语言的交互而DotNet Remoting相对于Web服务就像Asp相对于CGI那样实现了一种质的转变DotNet Remoting提供了一个可扩展的框架它可以选择不同的传输机制(HTTP和TCP是内置的)不同的编码方式(SOAP以及二进制代码)安全设置(IIS或SSL)同时提供了多种服务包括激活和生存期支持
远程方法调用
对于远程方法调用来说最直接的问题恐怕是一个本地方法推而广之一个本地对象如果放在网络环境中如何传递这个方法的调用返回如何传递这个对象的请求虽然对于应用开发人员来说这个并不是必不可少的事但对于我们学习分布式操作系统来说我想这是应该搞清楚的我们知道DCOM协议也被称为对象RPC它建立在DCE RPC协议基础上也就是说在网络传输这一层它必须使用特殊的协议另外Windows RPC 机制要求熟悉的类型和使用 IDL 工具的封送处理知识并向开发人员公开 RPC 客户端和服务器存根的管理Remoting 在为 NET 提供 RPC 时要容易得多而且由于使用简单易懂的 NET 数据类型从而消除了早期 RPC 机制中存在的类型不匹配的情况(这是一个非常大的威胁)配置为使用 HTTP 或 TCP 协议并使用 XML 编码的 SOAP 或本机二进制消息格式进行通信开发人员可以构建自定义的协议(通道)或消息格式(格式化程序)并在需要时由 Remoting 框架使用服务器和客户端组件都可以选择端口就象可以选择通信协议一样由此带来的一个好处是很容易建立并运行基本的通信
下图中描述了Net Remoting的五要素
代理在Client端伪装为Remote Objects并转发对Remote Objects的调用
Message消息对象包含了执行Remote Methods调用的必要数据参数
Message Sink/Channel Sink在Remote调用中Message Sink允许定制消息处理流程这是Net Remoting内置的可扩展特性
Formatter也是Message Sink用来序列化消息已适于网络传输如SOAP
Transport Channel也是Message Sink用来传输序列化的消息到远程进程如HTTP
当访问Remote Objects时Client端application并不处理真实对象的引用而是仅仅调用Proxy对象的方法Proxy对象提供与Remote Objects相同的接口伪装成Remote ObjectsProxy对象自己并不执行任何方法而是以消息对象(Message Object)的形式转发每一个方法调用给Net Remoting Framework
在类型支持方面DCOM提供了一套复杂的列集和散集机制他建立在RPC的基础上由于RPC被定义为DCE标准的一部分而DCE RPC定义了所有常用的数据类型的数据表达方法即网络数据表示法为了使存根(stub)代码和代理对象能够正确地对参数和返回结果也进行列集和散集它们应该使用一致的数据表示法NDR以便在不同的操作系统环境下也能够远程调用
This figure is from the book named Advanced Net Remoting
反过来我们再看看NET Remoting 强大的类型操作Net Remoting 支持所有托管的类型类接口枚举对象等这通常被称为多类型保真这里的关键在于如果客户端和服务器组件都是在应用程序域中运行的 CLR 托管的对象则数据类型的互操作是不成问题的从根本上讲我们拥有的是一个封闭的系统会话的两端可以完全被理解因此我们可以充分利用这一事实处理好用于通信的数据类型和对象
在各种系统并存的情况下我们需要考虑系统之间的互操作性对于可互操作的数据类型我们要谨慎处理例如Web 服务数据类型的定义要基于 XML 架构定义 (XSD) 关于数据类型的说明任何可以使用 XSD 进行描述并可以在 SOAP 上进行互操作的类型都可以使用但是这也确实使得某些数据类型不能使用
代理
代理对象伪装成一个远程对象并且向本地提供远程对象相同的接口客户端应用程序与处理远程真实对象的应用(包括内存引用指针等等)都是通过代理对象实现的代理对象当然并不自己完成方法调用它将请求作为消息对象传给NET Framework在这一方面Net Remoting 和DCOM思想上是一致的当某个客户端激活一个远程对象时框架将创建 TransparentProxy 类的一个本地实例(该类中包含所有类的列表与远程对象的接口方法)因为 TransparentProxy 类在创建时用 CLR 注册所以代理上的所有方法调用都被运行时截取这时系统将检查调用以确定其是否为远程对象的有效调用以及远程对象的实例是否与代理位于同一应用程序域中如果对象在同一个应用程序域中则简单方法调用将被路由到实际对象如果对象位于不同的应用程序域中将通过调用堆栈中的调用参数的 Invoke 方法将其打包到 IMessage 对象并转发到 RealProxy 类中此类(或其内部实现)负责向远程对象转发消息TransparentProxy 类和 RealProxy 类都是在远程对象被激活后在后台创建的但只有 TransparentProxy 返回到客户端在代理对象创建时需要引用Client端的Messag Sink ChainSink Chain的第一个Sink对象的引用保存在RealProxy对象的Identity属性如下图所示
所以在下面这段代码调用new或者GetObject获得对象后对象obj将会指向
TransparentProxy对象
HttpChannel channel = new HttpChannel();
ChannelServicesRegisterChannel(channel);
SomeClass obj = (SomeClass) ActivatorGetObject(
type of(SomeClass)
);
消息
当一次函数调用指向远程对象的引用时TransparentProxy创建一个MessageData对象并且将它传给RealProxy对象的PrivateInvoke()调用RealProxy相应地生成一个新的Message对象并且以MessageData对象为参数调用其InitFields()方法Message将会解析MessageData对象中地指针进行相应地函数调用当处理结束时将会返回一个包含回应消息的IMessage对象RealProxy将会调用它自己的HandleReturnMessage()方法该方法检查参数并且调用PropagateOutParameters()方法当处理结束后RealProxy将会从它的PrivateInvoke()方法中返回IMessage将会返回给TransparentProxyCLR保证函数返回值与其返回格式相符所以对于应用程序来说它仅仅认为这是一个一般方法的调用返回这正是分布式的最终要求
可以看到NET Remoting与DCOM的底层机制已经完全不同了DCOM的一次方法调用需要经过列集(marshaling)和散集(unmarshaling)的处理包括创建代理对象和转载存根代码等但是可以看到NET做为DCOM的下一代在基本思想上和DCOM是一脉相承的在我学习到现在的体会看来NET面向Web服务的RPC机制的确是一大革新让我们看看IMessage怎么进行传递的
这是一个消息穿过Transport Channel(也就是Message Sink)的实例
首先是SOAP远程调用的HTTP请求
POST /MyRemoteObjectsoap HTTP/
UserAgent: Mozilla/+(compatible; MSIE ; Windows ; MS NET
Remoting;
MS NET CLR )
SOAPAction:
#
setValue
ContentType: text/xml; charset=utf
ContentLength:
Expect: continue
Connection: KeepAlive
Host: localhost
然后是一个对于一个远程对象setValue(int):
POST /MyRemoteObjectsoap HTTP/
UserAgent: Mozilla/+(compatible; MSIE ; Windows ; MS NET
Remoting;
MS NET CLR )
SOAPAction:
#
setValue
ContentType: text/xml; charset=utf
ContentLength:
Expect: continue
Connection: KeepAlive
Host: localhost
<SOAPENV:Envelope
xmlns:xsi=instance
xmlns:xsd=
xmlns:SOAPENC=
xmlns:SOAPENV=
SOAPENV:encodingStyle=
xmlns:i=
>
<SOAPENV:Body>
<i:setValue id=ref>
<newval>