不知道是刻意或是纯粹出于偶然,营销名词和技术名称以及通用词汇竟然都在同一个时间点代表了同样的意义:过去与现在,传统与流行。
NET or COM
COM
,你可以有很多解释。如果你是个开发技术的钻研者,你很直觉的反应,就是
Component Object Model
的简写,代表了一个
Windows
平台上的对象通讯技术,前身是
OLE
,后来逐渐取代了
Windows
平台上的另外一个应用程序设计界面
Win32 API
,成为应用程序之间彼此通讯及互动的技术主流。但是如果您是一个投资者,
.COM
可能代表的是网络泡沫化之前最热门的投资对象。对现在以基本面为主的电子商务时代,它代表的是一个曾经令人疯狂的过去,曾经以技术凌驾实质经营内容考虑投资标的的时代。但若是换做一个纯粹的网络公民或是企业,
.COM
不过是网址的一部份,代表的是一个商业团体。不论是那一种解释,都是一种通俗、过往、平凡无奇,难以突显自我的感觉。
相反的,
NET
,如果从技术人眼光,看到是微软新一代的开发技术和平台,同时也如同过去的
OLE
一样,成为一个营销用的名词,让人在一开始搞不清楚究竟在微软的世界中,
.NET
涵盖的范围究竟有多大,多广泛。而从投资者的角度,
NET
代表的是后网络时代因特网的真正意义
─
加速沟通的平台,我们不会因为把生意摆上网络就赚大钱,但是有机会因为网络来强化商业竞争力。网络就是网络,网络不等于商务
(COMercial)
,但是商务有机会透过网络延伸。而在网络公民而言,
.NET
是
.COM
之外的另一个选择,特别是在大家都知道要靠网络打知名度,没有网址就不入流的时候,要找到好记的
.COM
网址已经不容易,就转向
.NET
甚至
.BIZ
之类的网域动脑筋了。
不过这个专栏毕竟是技术专栏,所以我们要讨论的
COM
或
NET
,都还是从技术的眼光着手。而在微软第一次提出
.NET
的愿景至今,已经有四年的时间了。经过四年毫不停歇的推动,我相信没有人再去怀疑是要坚守
COM
的技术领域,还是转换到
.NET
的环境,问题只在什么时候开始而已。而一旦决定了开始的时机,接下来的问题就会是如何开始?
这些问题,很难有个好答案。毕竟技术的转换,牵涉到诸多层次,而
.NET
和
COM
绝对不能说是同一个世界的好伙伴,这当中有太多观念上的差异和实作上的区别。因此,既有系统的整合、技术上的学习过程、工具上的改变
…
都会是考虑的因素之一,而这里面的每一个因素,都不是容易被决定的。从微软的角度,也坦承这绝对不是容易的事,微软所能做的,就是提供完整的蓝图和解决方案让这个转移过程能尽量平顺,包括提供合适的开发工具降低转换上的成本,或是在技术上提供彼此互通的能力,以便让两个世界的公民能顺利进行交流,不致损害到企业既有的投资。而这个专栏的目的之一,也是期望能为这件事情尽一点力,透过技术观点上的比较,让
COM
世界的伙伴能更快掌握到
.NET
世界的精神,并且以更顺利的方式进入到
.NET
的世界,善用
.NET
世界的资源。
从纯粹技术的观点,我并不是个专精的
.NET
开发人员,比起一些长期发表硬派文章的大师级人物,我还不具备深入剖析
.NET
架构或是技术精要的程度。不过做为加入
.NET
世界的先锋之一,也曾经是踏入
COM
世界的先锋,将自己的研究心得分享出来,并且尽可能清楚的陈述两个世界的差异,倒是自己可以做到的程度。所以我把这个专栏定位成从概念的角度去讨论
.NET
的技术,程序代码的功能则是辅助我们对观念的了解,这样也可以避开我不是专精程序设计的尴尬情况。
所以第一次,要谈什么?我想先从
.NET
技术中,较不被放在镁光灯下一个重要成员
─
.NET Remoting
开始。
跨计算机的沟通机制
如果要我对
COM
下一个定义,我大概会说
COM
是一个应用程序之间沟通的机制,让不同的应用程序能取得别的应用程序提供的服务,同时在两个应用程序之间进行沟通的作业。在这个过程中,牵涉到两个作业,第一个作业,是找到正确的沟通对象,并叫用正确的服务;第二个作业,则是顺利的将数据传递给对方,并取得对方的响应结果。
第一项作业的重点在定位,有没有你要的程序
(
更精确的说是组件
)
?这个程序有没有提供你呼叫的方法
(
或函式
)
?如果要你要如何顺利的引用到这个方法?它提供的定义和你以为的定义
(
也就是参数的个数、型别
)
是否相同
…
。
第二项作业的重点则在数据型别的转换。由于每一个应用程序可能是由不同的程序语言开发,因此在数据型别的处理上也不见得采用一致的命名原则或是运算原则。如果不能有一套方式将甲地的数据正确的转换成乙地能理解的方式,那即使能取用到对方的服务,我们也不能保证取得的结果是自己能使用的。所以一个数据型别的处理系统对于跨应用程序的通讯是相当重要的。
COM
提供了一个复杂而有效的系统解决这两个问题,让开发人员能够以更直觉
(
相较于过去的技术
)
、更简便的方式取用其它程序或组件提供的服务。由于它采取对象化的架构,因此在服务的提供和取用上,都远比过去以剪贴簿的概念提供通讯能力的环境,或是纯粹透过函式呼叫进行应用程序通讯的作法更有生产力,也更能够发挥重复运用的观念,缩短开发的时程。
不过
COM
的着眼点是在于同一台计算机上不同应用程序之间的通讯需求,跨到另外一台计算机之外,就不是一开始
COM
所设想到的领域。所幸跨程序的通讯和跨计算机的通讯差异仅在于通讯协议的处理
(
也就是定位问题
)
,对于数据交换上型别差异的处理并不会因此而有区别。所以要让
COM
的环境能更进一步延伸到跨计算机的领域,只要妥善解决计算机定位的需求,就有机会克服。同样幸运的是,
COM
在一开始的设计中完全不去碰触跨计算机的问题,使得要在
COM
的架构之上再架上一层跨计算机的处理环境并不会去破坏到原本的架构。于是
COM
的网络延伸版本
DCOM(Distributed COM)
就此出现,专责让
COM
组件可以在网络环境下持续提供服务。
DCOM
最主要处理的是两个议题,第一个议题是网络通讯能力,第二个议题则是权限的问题。之前
COM
是在同一台计算机中找特定的组件,而
DCOM
则要更进一步去找网络上的某台计算机,之后沿用
COM
的机制找到计算机上的组件。而另一方面,一个单机执行的应用程序基本上可以因为账户都在同一台计算机上,没有认证和权限判定的问题。但是从甲计算机跳到乙计算机,账户不见得通用,权限的原则也不会相同,这个时候如何能确认甲方提出要求的程序真的有资格使用乙计算机上的某项服务?如果处理不慎,等于为恶意的程序大开方便之门。这部份
DCOM
提供了必要的机制和 操作系统 整合,解决对方呼叫程序身份辨认的问题。
到了
.NET
当中,跨计算机的议题同样也需要对应的技术进行处理,
.NET Remoting
就是一个对应于
DCOM
的技术,它让存活在不同应用程序定义域
(AppDomain
,一个
.NET
中的新概念
)
、不同执行程序、以及不同计算机上的对象能够顺畅的进行沟通动作。在累积了长期以来分布式应用的经验之后,微软没有理由把东西设计的更难用。因此,您可以预期的,
.NET Remoting
提供了更具威力,也更易于使用的开发架构,来支持跨计算机的沟通作业,省却开发人员建立分布式应用程序时必须花费的心力。
您或许会问,如果
.NET Remoting
真如你所言的有威力,而分布式应用又似乎是目前商业软件开发上必然发展的趋势,为什么不是在镁光灯之下,成为众所瞩目的焦点?理由非常的简单,另外一个好兄弟抢走了所有的风采,它同样也提供跨计算机的沟通机制,同样提供了和
DCOM
或
.NET Remoting
近似的机制,更重要的是,透过它进行沟通的双方还可以不用都是
.NET
世界的成员。有了这样的兄弟,要不被抢走风采,难!没错,它就是
Web Services
。事实上,从
.NET
一问市,
Web Service
就一直在
.NET
的世界中占重要的地位,也一直是微软对外强调的重点技术。相对的,不论在微软大小场合,提到
.NET Remoting
的机会反而少很多。
Web Service
受到瞩目标机会较多另外还有一个很重要的理由,就是
Web Service
的概念很简单,而且完全不牵涉到功能面实作设计上的规范。
Web Service
是一套以
HTTP
和
XML
为基础的通讯机制,这两者对于绝大多数的操作系统或信息作业环境都是必备的要件,而且实作上不牵涉到很深奥复杂的概念。这种简单的特性让它可以在很短的时间内受到重视,并且被普遍应用。相较起来,要充份掌握
.NET Remoting
的特性,就得花费较多的时间去学习了。
既然如此,
.NET Remoting
看来不就是多余的,又何苦在
Web Service
之外再弄一套技术呢?事实不然,这两样技术看来要解决的问题相近,但却有应用环境上的差异。前面提到
Web Service
是一套以
HTTP
和
XML
为基础的通讯机制,而前者是采取无状态的方式作业,因此前端和伺服端建立的是一个松散的通讯关系,而且前后的呼叫执行作业之间是无关连的。而后者是一个以文字格式数据为基础的数据交换标准,文字格式数据从计算机的角度来看,是很难以最有效率的方式进行处理。所以对于一个前后端必须以有状态紧密结合方式进行互动作业,同时又期望呼叫和数据交换的动作上能以最有效的方式进行的环境而言,
Web Service
就不是一个理想的作业环境了。
我们可以用这样一个例子做比较,基于前后呼叫毫无关联的情况,当甲地的甲组件呼叫乙地的乙组件时,乙地必须为这个作业做好通讯的准备,然后再持续的接收到甲组件提供的数据。接着乙组件要执行逻辑,然后再把结果透过之前建立的网络通讯环境传递回甲组件。然后呢?甲乙两方都会放弃掉这个过程中所建立起的网络通讯管道
(
因为
HTTP
是无状态的设计,完全不会主动记住前一次通讯和后一次通讯的关联性的
)
。当下一次甲和乙之间又要通讯时,这样的准备作业和善后作业又要再重来一次。您可以想见,只要多有几次两地来回的互动作业,就等于消耗掉好几份珍贵的资源。
但是如果换一个情况,当甲和乙完成第一次交易后,因于甲方预知后续还会有互动的作业要进行,所以它不会释放掉网络通讯的管道,而乙方也会保留这一个管道,直到甲方认为已经完成所有的互动作业,主动通知乙方不再需要它的服务时,乙方才释放已经建立的管道。这时彼此间持续的互动就会在一个早已经准备好的管道上进行,不用重复上演取得管道、释放管道的戏码。这样的作业模式能节省多少资源,并且加快处理的速度?
就是因为这样的差异,
Web Service
并不能妥善应付持续性互动作业的需求,同时又因为数据格式上转换形成的负担,无法用最有效的计算机表达方式来进行数据处理,而
.NET Remoting
相对也有了生存的空间。一方面,
.NET Remoting
在进行数据传递时,是以二进制格式为基础,所以在数据的定义上能以最有效率的方式表达。另一方面,
.NET Remoting
是
.NET Framework
的一部份,自然就能充份使用到
.NET Framework
所提供的各项机制和功能。这一点是强调异质平台和开放标准的
Web Service
所不能及的地方。
简单讲,只要互动双方都是支持
.NET
技术的环境,并且在操作系统上能提供一致的验证机制,而且很重视沟通双方作业的效率,就是
.NET Remoting
上场的时机。相反的,如果沟通双方是异质环境,又或沟通的模式就是无状态的情况,持续进行的互动作业前后是没有关联的,
Web Service
就是理想选择了。
|
.NET Remoting
的对象类型
在
DCOM
的世界中,任何
COM
对象都可以透过系统配置的方式支持
DCOM
的使用,因此程序开发人员在设计 服务器 组件时并不需要特别为
DCOM
的环境做什么安排。而使用
DCOM
组件的前端程序则必须就叫用组件的动作上做一些修正,因为原本产生对象时都是对本机的环境进行处理,现在则必须在叫用
CreateObject
方法时也提供服务器的名称。
同样的情况,在大多数的情况下,我们可以透过配置的方式来安排一个
.NET
的对象可以供远程叫用,而不需要在程序中做特别的修正。掌握这个原则,我们就能理解在大多数的情况下,当我们期望某个
.NET
类别对象能对远程提供服务时,大概只要透过一些设定配置的作业就行,牵涉到程序修正的情况较有限。所以在我们更进一步说明如何让类别具备服务远程的能力之前,先来看看
.NET Remoting
对象的类型和相关的议题,之后再来看设计或配置上的细节。
能够做为
.NET
远程对象的对象依照它的作业形式大概可以分成三种类型,分别是:
- 单次呼叫对象(single call object):它只服务一次的叫用,被叫用一次之后就被释放。也因为这种只叫用一次的特性,它并无法为前端保存状态。从前端的角度来看,即使是对这类对象的一个实例持续叫用了不同的方法,在服务器端也都是独立的为每一次的呼叫准备一次对象,各个对象的产生都是独立的行为,和前端是否是对同一个对象变量进行呼叫无关。以过去COM的术语,就是所谓无状态组件(stateless component)。
- 单一物件(singleton object):它是可以同时服务多个前端的对象,所以即使在不同的前端启动这种对象的实例时,应该是属于不同的实例,但在服务器端实际上只产生了一个对象的实例,同时应付多个前端的要求。所以不同的前端是共享同一个对象,而对于需要在不同前端之间共享信息的情况,这种对象能够很方便的提供这类型的功能,同时也节省服务器端的资源。
- 前端启用物件(client-activated object,简称CAO):这类型的对象是在前端提出要求时才会启用,它十分近似COM的标准类别。一个前端启用对象只服务一个前端,并且在前端未释放的情况下都是持续存在,所以它能够保存前后叫用之间的状态。
前面的两种对象由于管理的作业都是以服务器端为主主动出击,所以也称做伺服端启用组件
(server-activated object
,简称
SAO)
。不同类型的对象,对于资源的耗用会有不同的影响。我们必须依据作业类别的特性决定要采用的模式,否则很可能会产生不利的结果。举个例子来说,前端启用对象的设计方式和一般类别的设计方式相仿,因此设计起来较不复杂,可以直接沿用已经熟悉的概念。但是由于它的生命期是由前端决定,当前端不能有效的使用对象,让作业密集的进行时,就很有可能让伺服端的对象不断处在等待指示的状况,而这个等待的过程同样耗用掉伺服端的资源。如果服务器要面对的前端数量众,看着闲置的资源无法运用并不是件好事,也降低了服务器的延展能力。在这样的情况下就有必要考虑将类别设计成单一对象或是单次呼叫对象的型式。
但是单次呼叫对象和单一对象的设计上有一定程度的复杂性,前者不会保存前端呼叫的状态,所以要不就是把责任丢给前端,要不就是我们自己要提供。而后者则会牵涉到同时服务多个前端,会不会有同步或错乱的情况。而且为了有效提供服务,通常还会有多执行绪的需求,这又是个复杂的主题。因此如何在设计的简易程度和执行效能之间取得平衡,就要靠设计者的考虑了。
远程对象
对于一个处理远程作业的基础架构而言,最重要的就是提供一个基础建设,将远程通讯的细节隐藏起来,让前端能够顺利的透过方法呼叫取用到远程对象的服务,并且传回结果。而当我们讨论到「远程」这个字眼时,有必要加以厘清。在
.NET
的环境中,除了「执行绪
(thread)
」、「执行程序
(process)
」之外,引进了另外一个执行单外,称做应用程序定义域
(AppDomain)
。你可以把应用程序定义域想象做之前的执行程序,只是执行程序或是执行绪都是对应到操作系统的层次,而且多半和硬件的环境
(
就是处理器
)
相结合,做为资源分配的单位。
在
.NET
中,为了更有效的进行资源分配,同时结合上充份管制资源运用
(
尤其是内存的使用
)
的新架构,提出了应用程序定义域这个执行单位。不同的应用程序是执行在不同的应用程序定义域之中,而数个应用程序可以同时处在一个执行程序之中。这样一来,可以减少执行程序的切换,而内存空间的使用也可以更有效率。但是那些应用程序定义域存活在那些执行程序之内并没有必然的关系,所以从开发人员的角度,将不再需要去关心执行程序,而是把重心放在应用程序定义域上。事实上,你应该直接摆脱执行程序的概念,什么内存空间、执行程序切换等等,因为对于受管制的
.NET
应用程序而言,这都是我们不需要去直接控制的项目。而在
.NET Framework
中,也提供了一个
AppDomain
类别来对应到应用程序定义域,让我们取用定义域中的资源或信息。
对于在同一个应用程序定义域的对象而言,我们可以称做近端对象,所以相对的远程,就是任何跨应用程序的情况,只要是另一个应用程序定义域的对象,相对都是远程对象,即便是同一台计算机的别的应用程序定义域中的对象,都还是视为远程的对象。
有了这样的认知,就能够理解
.NET Remoting
所处理的其实并不只是跨计算机的情况,它也可以处理本机的环境。只是在一般单机的应用程序中,
.NET
的应用程序多半是执行在同一个应用程序定义域中,所以没有感觉而已。但是如果我们从一开始就决定开发分布式的应用程序,想要在同一台计算机上先进行测试,就可以利用
.NET Remoting
的技术,将前端和伺服端的程序执行在不同的应用程序定义域中,并且透过配置文件建立起沟通的环境。之后一旦将伺服对象移到别的计算机,只要更改配置文件的设定,就能够再度建立起通讯的能力。所以当我们讨论到远程对象时,请务必记住,只要跨应用程序定义域就是远程了。
当我们谈到远程对象时,在用法上可能会有两种想法,第一种是很直觉的,在远程提供服务的对象。这在种情况下,对象是在服务器端进行作业,使用到的是服务器上的资源,如果它只进行运算作业,使用到的就是服务器上的处理器能量或是内存;如果它还提供数据储存的服务,则可能在这些资源之外还使用到硬盘的空间。
另外一种情况,我们使用的不是单纯它的服务,而是传递对象来达成服务的目的。例如将一个
DataSet
从一台计算机传递到另一台计算机,这个
DataSet
对象同样也是远程对象,因为它的来源是另一台计算机。
如果只是单纯取用服务,在对象的设计上并没有什么特异之处,只要注意要采取的服务提供模式
(
单次呼叫、单一实例、前端启动
)
就行。但是如果谈到的是跨应用程序定义域的传递,情况就不太一样。对于在同一个应用程序定义域的对象而言,从甲方法传递到乙方法时,传递的是对对象的参照
(
传址
)
。而如果是传递纯量的数据,则是传递实际的值
(
传值
)
,而不是参照。但是只要是跨定义域的传递,一律都是传值的方式处理,无法以传址的方式处理。作业的模式是将对象的内容以序列化的方式传出,到了前端时再将接收的内容整理重建出该对象。所以能否将内容序列化的传递,并且提供足够的信息让远程环境重建相同内容的对象就成为是否能被传值传递的关键。因此任何一个要能被传递的远程对象都必须以属性卷标
[serializable]
标示,或者是要实作
ISerializable
接口。所以只要无法序列化的对象,都无法跨应用程序定义域传递。