UML软件工程组织

分拆与组装解决COM+的通讯问题(1)
作者:Mac 本文选自:赛迪网 2003年02月10日

 

看到这个题目,大家千万不要以为来了一位机械工程师,要为大家讲解机器的分拆和组装。今天只是一位软件工程师要在这里为大家唠叨一个算是老生常谈的话题:在COM+应用程序中客户端与应用服务器端的通讯问题。


问题的提出


在转入正题之前,先来说说我今天逛街的经历。天气逐渐转凉,我准备为自己采购点过冬的装备。我先来到一家路边小店,挑了一条白色的围巾,一番讨价还价之后,一手交钱一手交货,搞定。随后我又来到上海最大的Super Brand Mall,东看西逛之后,看中一件黑色的外套,准备付钱时,营业员小姐递给我几张票据然后很有礼貌地请我到收银台付款。经过一番折腾之后,总算拿到自己的东西。虽然是简单的购物经历,仔细想想,还是能够发现有点小小区别:在小商铺买东西的时候,所有的交易都在老板一个人那里完成,而在购物商场里则不然,要反复折腾两个地方,和不同的漂亮女孩打交道。其实,在我们的软件领域里也存在着类似的现象。我们知道,如今的软件架构有两层的C/S方式,也存在着三层的B/S架构直至多层架构。而且随着分布式系统的流行,越来越多的系统采用了多层架构的模式。

层数增加了,问题自然也就出来了。各个层之间要相互协作,就不可避免要互相通讯。在前面的购物过程中,当我去付款时,售货员给我几联发票,我拿着发票来到收银台,收银员根据发票上记载的信息收取相应的金额,然后在发票上盖上公章,然后我再凭着已加盖公章的发票到售货处领取我所选购的货物。同样在我们的COM+分布式应用当中,客户端与应用服务器端之间也存在着如何通讯的问题。微软提供的DCOM可以用来解决这些通讯问题,在客户端与服务器端之间,你可以通过多种形式来传递信息:XML字符串,记录集对象,各种类型数组,XML文档对象等。

解决了通讯问题,接下来就是如何来优化这种通讯机制,即安全可靠又不失高效快捷?也许我们应该说使得这种通讯性能能够让我们接受,因为许多系统低效的通讯方式产生的让人难以忍受的响应速度打击了许多的用户。试想一下如果哪个商场的收银台前每次都是排着一条条长龙,估计是有很多象我这样的顾客要跑到别家商场去的。


几个基本原则


明确了面临的问题,接下来就需要寻找相应的解决方案。既然问题是从现实中来,那我们就尝试着到现实中去寻找答案。我们来分析一下隐藏在简单的购物过程后面的一些道理。首先,我们看到在整个购物过程中发票是作为一种购物信息载体的形式出现的。本来我们购买的是衣服、鞋子等物品,应该拿着这些去收银台付款的,但是那样显然非常麻烦,所以有了发票的产生。这就给了我们第一个启示:不要使用大而笨重的衣服,尽量使用小巧方便的发票。回到我们的应用系统中,我们也可以得出这样一个结论:在层间进行通讯时,不要直接使用对象(大而且耗费传输时资源),尽量使用相应的替代物,比如字符串之类。由于对象的属性是单个地进行传递,而且每次都必须经过列集(marshaling)、传递和参数散集(unmarshal)的过程,所以在层间通过DCOM方式传递对象非常地慢。如果说单个的对象还不是很明显的话,那么一大堆的对象传递起来肯定会让你受不了。

在整个过程中,发票是如此的重要,那我们得好好地研究一下它。首先,你可以看到在发票上一般填写以下几个栏目的信息:品名规格、单位、数量、单价、金额和合计人民币等几项,还有一项我想特别提出的就是:收款方式(可能老式的发票没有这一项)。为什么这些付费相关的信息却要在去收银台之前就被柜台营业员询问并填写好呢?这可值得我们好好来分析一下:在商场中一般只是每层设立一至两个收银台,而营业员一般每个柜台就有一至两个。所以造成排队的地方很有可能在收银台处,如果每次收银员小姐还要计算价钱和询问"先生,请问您用什么付款?"的话(有点类似于麦当劳的味道),那只能是长龙越来越长,这样使用发票换来的效率都用在增加长龙的长度上啦,最终的结果只能是顾客牢骚满腹。所以每次柜台营业员尽可能地将顾客所购买的商品信息以及付费信息都了解清楚,这样收银员就可以直接来处理最后的结果。上面我们已经说过,在我们的应用系统中,我们可以利用字符串等来代替对象的直接传递,这里我们需要加上另外一点:在层间进行通讯时,信息被传递之前,必须确信已收集到足够的有用信息。尽量避免层间过多的不必要的反复过程,尤其是在那些系统瓶颈(system bottleneck)之处。

同时,我还发现每次售货员小姐把付款发票交给我以后,就接着去招待别的顾客,而不是一直在等着我来付款提货。要是那样的话,我是很乐意的,但如果我中途一不小心去看了场时装秀的话,那整个店铺的生意就没法做啦。所以说,应该尽量避免这种对时间和顺序的依赖性。用计算机的术语来说就是:尽量避免使用同步调用,多考虑使用队列组件等异步调用方式。

此外,在某些编程语言(比如VB)中,参数可以按照两种不同的方式进行传递:按值传递(ByVal)和按地址传递(ByRef),后者更是VB中参数传递的缺省方式。本来按地址传递仅仅需要传递参数对象的指针,从而避免参数对象的拷贝。但是这里面却省却了一个前提条件,就是调用者与被调用者必须处在相同的进程中,这样才可以通过指针找到相应的对象。但是,在我们的COM+应用程序中,常常发生客户端组件与服务端组件分处不同机器的情况,如果这个时候依然采用按地址传递的话,会发生什么情况呢?因为指针不能跨越不同的进程,所以这个时候系统实际上也是采用将整个对象拷贝的方式,而且调用结束后,需要将相应的对象拷贝回调用者。读到这里,你肯定发现按地址传递这个时候反而增加系统的工作量。所以说:在实现COM+应用系统的时候,如果不是确实必要,尽量避免使用按地址传递参数的方式。这样就可以避免对象来回进行的散列(marshalling)和传递过程,同时也避免了不同层间的耦合性以及某些安全方面的问题。

以上的这几点启示都是我们从一次普通的购物过程中悟出来的,其实现实中任何事物都蕴藏着许多很伟大的道理,细心解读它们能够获得很多受益。由于其它的内容在本文讨论范围之外,所以只能忍痛割爱不再细讲。(1)


现有的解决方案


重新回到本文开头提出的问题中来,我们现在可以遵从上面给出的几条准则来提出我们的解决方案。在这之前,先让我们来看看现有的一些主要的解决方案。

1、使用XML

优先使用XML字符串(XML String),而不是XML文档对象(XML DOM Objects)

2、记录集对象(Recordset)

主要采用非连接方式的记录集对象(Disconnected ADO Recordsets)。你可以通过设置Recordset的CursorLocation属性为客户方游标来获得。此时,在层间传递的是整个记录集对象,而且对记录集使用的锁定类型有相应的限制。

3、数组

可以利用记录集对象的GetRows方法直接获得

关于以上各种解决方案的详细说明以及各自的优缺点,参考1中Edward A. Jezierski的文章有足够详细和精彩的讲解,在这里就不再赘述。但是这几个解决方案好像主要是针对数据库对象的,先从数据库获得数据,然后直接利用记录集对象或者简单地将之转化为XML字符格式或数组,最后传递给调用者。这里好像忽略了另外一个庞大的群体:类对象。我们知道,随着面向对象(OO)思想的广泛应用,越来越多的系统里使用了类和对象。那么如何在层间来传递这些对象呢?好像还没有一个很好的办法。在这里我想向大家介绍一种新的思路,用来解决层间类对象的传递问题。



版权所有:UML软件工程组织