我所理解的Remoting(1):Marshaling & Activation[上篇]
什么是Marshaling &Activation
对任何一项分布式技术(Distributed Technology),比如Remoting,XML Web Service,Enterprise Service,Marshaling和Activation(对于Marshaling,我实在是找不到一个比较贴切的中文短语来翻译,很多书把它翻译成封送,我总觉得很别扭,所以在这里我就直接用英文Marshaling,如果读者有较好的翻译,麻烦通知我一下)都是必须要解决的问题。本Blog主要讲述的是在Remoting中的Marshaling和Activation。
首先我们来讲讲到底什么是Marshaling和Activation。我想对于这个问题每个都心中都有自己的定义。我是这样理解的:对于一个对象,当他创建的时候被绑定到(Be Bound)到某一个Context上——这个Context可能是一个内部网络,一台主机,一个进程,一个托管的Application Domain。当另一个Context要调用这个对象,有时候必须 对这个对象作出一些不要的转变(Transformation),这个转变的过程被称为Marshaling。我们一般由两种方式的Marshaling——By Reference 和By Value。前者向是把对象的一个引用传递出去,而后者则是从新创建一个和对象一样的Copy向外传递。
对于任何一个分布式应用来说,Client和Service都分别出于各自的Context之中,Client 以某种方式调用Server(可能是基于Message, 也可能基于RPC)Server),这个调用请求通过一个已经注册到Server端注册的Channel传递到Server端,Server从这个调用请求中提取所需的Metadata信息,创建相应的对象——对Client来说这个对象是一个远程对象(Remote Object)而Activation就是如何创建Remote Object得过程。
Hosting
一个Remote Object能够被不同的Client调用,首先它必须Host在某一个进程之中,对于Remoting来说,你可以选择有很多种选择方式——你可以选择任何一种Managed Application来Host你所需要的Remote Object——Console Application,Windows From Application,ASP.NET Application,一致于Windows Service 中,我们把这种Host方式Self-Host。你也可以把它Host到IIS (6.0 &7.0)以致WAS(Windows Activation Service)。
这个Host的过程本质上包括下面两个方面:
Channel Registration:Channel是Client调用Server的通道,Client调用某个Remote Object,这个调用请求首先转化成一个Message,这个Message通过Client选择的一个Channel从Client AppDomain传递到Server Appdomain,同理执行执行的结果(Result)也以同样的方式从Server Appdomain传递到Client AppDomain。这里有一个根本的前提,Client选择的Channel必须先在Host中注册过。
Object Registration:Object Registration的本质就是把Remote Object相关的原数据(Metadata)注册到Host环境中,并为它制定一个Objet URI。如果说Channel Registration结果了如何Communication的问题,Object Registration可以看成是解决如何创建Remote Object和验证调用的合法有效性问题——它利用MetaData来创建Remote Object和验证Client端的调用请求。MSDN把这个过程称为Object Registration,我实际上不太赞成这种说法,因为这个过程做的仅仅是注册Remote Object Metadata的信息——实际上就是Type的信息,中间不曾有过对象的创建。所以我觉得叫做Remote Type Registration更加准确点。
当完成Object Registration之后,Remoting Framework根据注册信息找到Server对应的Assembly,从而提取出所需要的Metadata,结合注册的Object的Uri 、Assembly Name、Channel相关的信息,创建一个类型为ObjRef的对象,这个对象基本上包含了能够调用对应Remote Object的所有信息(关于ObjRef,下面的章节会后介绍,如果想查看详细的信息,你可以参考MSDN)。Remoting Framework内部维护着一个Table用于存储他所有注册的类型。
Proxy
在托管的环境(Managed Environment)下,Application Domain把一同的每个Managed Application隔离在它们各自的区域内,在一个Application创建的对象不能被另一个Application所直接调用。他必须通过Marshaling以传递引用或者传递从一个Application Domain传递到另一个Application Domain中。关于Application Domain的隔离性可以参照我的文章([原创].NET Framework——用Coding证明Application Domain的隔离性 )。
对于Remoting来说,Remote Type继承的是System.MarshalByRefObject,从名称就可以看出,它是以传递Reference的方式来Marshal 的。为了使我们更加准确地理解MarshalByRefObject,我们需要引入一个新的类System.Runtime.Remoting.ObjRef。ObjRef是一个可序列化的对象,用于扩展MarshalByRefObject对象(MRB Object)。当一个MRB Object被Marshal的时候,实际上Remoting Framework会根据MRB Object创建一个ObjRef,这个ObjRef包含了Client调用此Remote Object的一切信息——Remote Object对应的Type信息;Remote Object实现的Interface;调用这个Remote Object所用的Channels;以及Remote Object所在的Uri。由于ObjRef是可序列化的,所以他以传值的形式传递到Client Application Domain ——Remote Object以Marshal By Reference的形式通过ObjRef实现的Application Domain之间的传递,而ObjRef本身则是以Marshal By Value的形式传递的。当此ObjRef到达Client Application Domain后,会在Client端创建一个Proxy,通过这个Proxy便可以远程地调用Remote Object了。
接下来我们结合图来了解具体的调用过程:
在Client Application,一个Client Object调用一个Transparent Proxy,这个Transparent Proxy把这个调用转换成一个IMessage对象,并把这个IMessage对象传递给RealProxy 对象,RealProxy调用Invoke方法并把该IMessage对象传递给Invoke方法。RealProxy调用CreateObjRef方法得到Remote Object的ObjRef,并通过Client注册的Channel把这个调用传递到Server Application Domain。
Activation
.NET Remoting有两种不同的Activation方式——Server Activation 和Client Activation。
Server Activation:客户端一般通过Activator的静态方法GetObject方法在Client端创建Transparent Proxy 和Real Proxy,Transparent Proxy被最终传道给Client。这里有非常重要的一点,通过上面的分析,我们知道,Proxy的建立需要Remote Object的ObjRef,而此时这个ObjRef处在Server端的AppDomain中,在创建Proxy的时候,Client是否会为了获取Remote Object ObjRef 而进行网络连接呢?答案是否定的,在Client端创建Proxy的时候,是不会使用任何对于Server的网络访问的。而创建Proxy所必需的是在Client端获取的——我们可以用编程和配置信息的方式为他指定Remote Object Metadata的信息(我们可以传入Remote Object Type 或者Remote Object Type实现的Interface)和Remote Object的Uri。而通过在Client端创建的ObjRef和通过网络访问从Server端获取的具有相同的功效。
当Client的Transparent Proxy创建了以后,这个Transparent Proxy就成了Remote Object 在Client端的代理。我们调用Transparent Proxy的某一个方法,Transparent Proxy会先把这个调用转化成一个Message(实现了IMessage Interface);然后调用Real Proxy (默认的是一个System.Runtime.Remoting.Proxies.RemotingProxy对象)的Invoke方法,同时把Message传入该方法。Real Proxy 调用GetObjRef方法获得Remote Object Metadata的信息来验证这个请求,验证失败,抛出异常。然后Real Proxy判断调用的对象是存在于和自己相同的AppDomain(MRB 不单单是用在跨AppDomain调用的场景),如果是直接在本地创建一个对象,执行相应的操作,把执行结果通过Transparent Proxy返回给Client。如果判断结果表明是一个远程调用,则通过ChannelInfo属性,获取Channel的信息,最终把Message通过Channel传递到Server端——这中间回经历序列化和编码等操作。
前面我们讲过,当Remote Obejct被Host的时候,Remoting Framework 会创建一个内部的Table用于记录他所有被注册的Remote Object Type。一旦完成了Host,Server端便开始利用所注册的Channel进行监听。一旦他监听到某一格来自Client端的请求,他把Message 截获下来,获取Remote Object(对于Client来说)的ObjRef,并同这个内部表的Items进行比较,从而知道需要激活的对象。如果Mode 是SingleCall创建一个对象,执行相应的操作,返回结构后销毁该对象。如果是Singleton模式,会判断相应的对象是否存在(这个说法不太准确,应该说是否有相应的对象存在,并没有标记为过期——具体的原因,可以留意我的下一篇Blog——关于Remoting的Lifetime Management),如果存在则直接调用该对象,如果不存在则重新创建,然后执行相应的操作。对于Singleton模的下的对象,其生命周期通过LifetimeManager来控制,这是一个机遇Lease的控制策略。
Client Activation:前面我们花了大量的篇幅来解释Server Activation,其中一个主要的特点是,Remote Object在第一次调用时激活,而不是在Client创建Proxy的时候激活。正因如此,对于一个Server Activated Object来说,它的对象的创建之只能通过默认的无参构造函数来创建。任何有参构造函数的定义没有任何意义,并且没有定义无参构造函数在调用会抛出异常。
相对于Server Activation,Client Activation采用了完全不同的激活方式。在Client Activation方式下,当Client调用New或者Actiovator.CreateInstance方法(再这之前,Client必须在Client端注册Channel和Remote Object Type),Remoting Framework会在Client段创建一个Activation Proxy,这个Activation Proxy远程地调用Server端的一个Activator远程对象,这个Activator对象激活相应的对象,创建相应的ObjRef传递到Client端,Client端利用这个ObjRef创建Real Proxy 和Transparent Proxy。至此Client端就可以通过该Transparent Proxy进行远程调用了。
从这个过程中我们可以看到,Remote Object实在Client创建Proxy的时候同时创建的,所以创建Proxy时指定的信息可以 传递到Server端,所以对于Client Activated Object,他们是可以由自定义参数的构造函数的。
- 全球15%工作将被自动化,中国1亿人将面临失业
- ASP.NET MVC的Model元数据提供机制的实现
- 清官难断家务事,人工智能却来介入家庭伦理大戏
- 使用Symfony的Console组件构建命令行程序
- 微软编程教育都在搞什么?从code.org到makecode,从Minecraft到Micro:bit
- 谷歌:通往完全自动驾驶之路
- 随时随地部署Kubernetes
- 使用CoreOs,Docker和Nirmata来部署微服务风格的应用程序
- 使用ACS和Kubernetes部署Red Hat JBoss Fuse
- 教你快速安装OpenShift容器平台3.6
- 面向开发者的Cloud Foundry
- 云数据库安全与农场和餐馆:知道来源的重要性
- 云数据库安全,农场和餐馆:知道你的来源的重要性
- NO.32 不堪重负:线程池拒绝策略
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- java安全编码指南之:表达式规则
- java安全编码指南之:Number操作
- 如何提高代码质量
- 小姐姐非要问我:spring编程式事务是啥?
- 阿里3面:Spring声明式事务连环炮,让我措手不及。。
- 旷视科技|商用端侧Raw图像降噪方案
- Python | 时间戳转换
- Stata | 2020年国家社科基金立项名单分析
- 还在用Guava Cache?它才是Java本地缓存之王
- 死信队列的消息处理方案
- 【Ceph】Rook 中使用 External Cluster 的方式管理外部集群
- Redis排行榜的设计与实现
- 血的教训 | 一次订单号重复的事故差点被开除
- 60分钟看懂HMM的基本原理
- R语言确实会蛮耗费磁盘空间哦