1 应用模式语言开发应用级网关

Douglas C. Schmidt

 

  通信应用的开发者必须致力于应对在开发中反复出现的、与效率、可扩展性和健壮性相关的设计挑战。这些挑战常常独立于应用特有的需求。成功的开发者通过应用适当的模式和模式语言来解决这些挑战。但是,传统上,这些模式被锁在专家级开发者的头脑里、或是深埋在复杂的系统源码中。本论文的主要目的是描述一种作为面向对象通信软件基础的模式语言。除了描述该语言中的各种模式,论文还阐释了为什么了解模式间的关系及权衡可以帮助指导可复用通信构架和应用的构造。

 

1.1 介绍

 

  通信软件是一组构造现代分布式系统及应用(比如Web服务、分布式对象、协作式应用,以及电子商务系统[1])的服务和协议。构建、维护和增强高质量的通信软件是困难的。开发者必须对许多复杂问题有深入了解,比如服务的初始化和分布、并发控制、流控制、错误处理、事件循环集成,以及容错。在由有经验的软件开发者所创建的成功的通信应用中必定包藏着这些问题的有效解决方案。

  将成功的通信软件解决方案的本质与特定实现的细节分离开来并非是不重要的。即使软件使用组织良好的面向对象(OO)构架和组件编写,要确定关键的角色和关系仍有可能是很困难的。而且,操作系统(OS)平台特性,比如多线程的有无;或是应用需求,比如最优努力 vs. 容错错误处理,常常是不同的。这些不同可能会掩盖在同一领域的不同应用的解决方案之间、其底层体系结构的共性。

  捕捉成功的通信软件的核心共性是相当重要的,因为:

 

1.    它为负责增强和维护现有软件的程序员保存了重要的设计信息。这些信息常常只驻留在起初的开发者的头脑中。因此,如果没有明确地为这些设计信息编写文档,它们就有可能随时间而消逝,从而增大软件的熵,并降低软件的可维护性和质量。

2.    它有助于对正在构建新通信系统的开发者的设计选择进行指导。通过为它们的领域中常见的陷阱和缺陷编写文档,模式可以帮助开发者选择适当的体系结构、协议和平台特性,而不用将时间和努力浪费在(重新)实现低效或易错的解决方案上。

 

  本论文的目的是通过例子来演示捕捉和传达成功的通信软件的本质的途径;在文中描述了用于构建应用级网关gateway)的模式语言,该网关在分布在整个通信系统中的对端peer)之间进行消息路由。模式所表示的是构建软件时所发生的问题的成功解决方案[2]。当相关模式被编织在一起,它们就构成了一种语言,有助于:

 

为讨论软件开发问题定义词汇表;以及

为这些问题的有序解决提供步骤。

 

  通过确定通信软件开发中的基本挑战,研究和应用模式及模式语言可帮助开发者增强他们的解决方案的质量。这些挑战包括开发者之间的体系结构知识的交流;适应新的设计范式或体系结构风格;消除非功能性的压力,比如可复用性、可移植性和可扩展性;以及避开通常只能通过昂贵的“试-错”方法来学习的开发陷阱和缺陷[3]

  本论文根据模式语言来介绍应用级网关的OO体系结构和设计;该语言用于指导可复用和网关专用的构架及组件的构造。应用级网关对于可靠性、性能和可扩展性有着严格的要求。因此,它们是介绍出现在大多数通信软件中的关键模式的结构、参与者和效果的极好样本。

  本论文中描述的模式语言是在构建广泛的通信系统时揭示的,其中包括在线交易处理系统、电信交换管理系统[4]、电子医学成像系统[5]、并行通信子系统[6]、航空任务计算[7],以及实时CORBA对象请求代理(ORB[8],等等。尽管这些应用中的特定需求不一样,其通信软件设计挑战却是相似的。因此,该模式语言含有专家的设计经验,可被广泛地复用于通信软件领域,其范围远远超出了在本论文描述的网关例子。

  本论文的余下部分组织如下:1.2概述应用级网关的OO软件体系结构;1.3使用应用级网关作为例子,检查该模式语言中的模式,它们构成了可复用通信软件的基础;1.4对这些模式和文献中的其他模式进行比较;1.5给出结束语。

 

1.2 应用级网关的OO软件体系结构

 

  网关是使网络上相互协作的对端去耦合、并使它们进行交互、而又不直接相互依赖的中介者[2]。如图1-1所示,通过网关路由的消息含有封装在路由消息中的有效负载。图1-2演示应用级网关的软件体系结构中对象之间的结构、关联,以及内部和外部动力特性。该体系结构基于为多种研究和产品通信系统开发网关所积累的大量经验。在我们构建了许多网关应用之后,下面的事实变得清楚起来:它们的软件体系结构极大地独立于用于将消息路由给对端的协议。这样的认识使得图1-2中描述的组件已为其他的数千通信软件项目所复用[1]。能如此有系统地复用这些组件的能力源于两个因素:

 

1.    对通信软件领域中关键模式的动作和交互的理解。模式在比源码和OO设计模型(它所关注的是个体的对象和类)更高的水平上捕捉软件体系结构中的参与者的结构和动力特性。在本论文中描述的某些通信软件模式已经分别建立了文档[1]。尽管个体的模式描述捕捉了有价值的专家设计经验,然而复杂的通信软件系统常含有许多模式;理解这些模式之间的关系对于推动解决在构建通信软件时所产生的巨大挑战、以及相应的文档编写来说是必需的。因此,1.3根据一种用于通信软件的模式语言来描述这些模式之间的交互和关系。在该语言中的模式协同工作来解决通信软件领域里的复杂问题。

2.    对实现这些模式的OO构架的开发。识别在许多通信软件系统中普遍出现的模式有助于使可复用构架组件的开发成形。本论文所基于的网关系统使用ACE自适配通信环境构架[9]来实现,ACE提供了集成的可复用C++包装外观(wrapper fa?ade)和组件来完成常见的通信软件任务。这些任务包括事件多路分离、事件处理器分派、连接建立、路由、应用服务的动态配置,以及并发控制,等等。此外,ACE构架还含有1.3描述的模式实现。但是,这些模式比它们在ACE中的实现要丰富得多,并且已被其他许多通信系统所应用。

 

 

1-1 对端和应用级网关的结构和动力特性

 

  这一部分描述怎样复用和扩展多种ACE组件,以实现图1-2所示的通信网关中的应用无关的和应用特有的组件。在此综述之后,1.3检查在ACE组件之下的模式语言。

 

 

1-2 OO网关软件体系结构

 

1.2.1 应用无关组件

 

  图1-2中的大多数组件都基于ACE组件,后者可被复用于其他通信系统中。唯一不能被广泛复用的组件是SupplierConsumer Handler,它们实现的是与消息格式和网关的路由协议有关的应用特有的细节。网关中应用无关组件的行为概述如下:

 

进程间通信(IPC)组件:SOCK StreamSOCK ConnectorSOCK Acceptor组件封装Socket网络编程接口[9]。这些组件使用包装外观模式来实现[1],该模式通过使开发者与低级、乏味和易错的Socket级编程屏蔽开来、简化了可移植通信软件的开发。此外,它们构成了下面描述的更高级的模式和ACE组件的基础。

 

事件多路分离组件:Reactor是基于1.3.3描述的反应堆模式的OO事件多路分离机制。它通过单一的多路分离点来在事件驱动的应用中引导外部的刺激。该设计允许单线程应用高效地在事件句柄之上等待、进行事件多路分离、并分派事件处理器。事件指示某件重要的事情已经发生,例如,新的连接或工作请求的到达。网关中主要的事件源是(1)连接事件,指示建立连接的请求,以及(2)数据事件,指示封装各种有效负载的路由消息,比如命令、状态消息和大块数据传输。

 

初始化和事件处理组件:在端点间建立连接涉及两种角色:(1被动角色,在特定的地址上初始化通信端点,并被动地等待其他端点连接它,以及(2主动角色,主动地发起连接到一或多个扮演被动角色的端点。Connector(连接器)和Acceptor(接受器)是分别为初始化网络服务而实现主动和被动角色的工厂[2]。这些组件实现1.3.5中描述的接受器-连接器模式。网关使用这些组件来与对端建立连接,并生成初始化了的SupplierConsumer Handler

  为增加系统灵活性,连接可通过以下两种方式建立:

 

1.    从网关到对端,常常在网关最初启动时、为建立对端的初始系统配置而进行。

2.    从对端到网关,常常在系统运行后、无论何时有新对端想要发送或接收路由消息时进行。

 

  在大型系统中,可能会有数打或数百对端连接到单个网关。因此,为加速初始化,网关的Connector可以异步地、而不是同步地发起所有连接。异步有助于在长延迟路径上(比如在卫星或长距陆基链路上构建的广域网(WAN))降低连接响应延迟。在Connector中包含的底层SOCK Connector[9]提供低级的异步连接机制。当SOCK Connector经由TCP连接两个Socket端点时,它生成SOCK Stream对象,并将其用于在对端和网关之间交换数据。

 

消息多路分离组件:Map Manger(映射管理器)高效地将外部id(比如对端路由地址)映射到内部id(比如Consumer Handler)。网关使用Map Manger来实现Routing Table(路由表),后者在内部处理发给网关的消息的多路分离和路由。Routing Table将对端发送的路由消息中包含的寻址信息映射到适当的Consumer Handler集。

 

消息排队组件:Message Queue(消息队列)[9]提供一种通用的排队机制,由网关用于在消息等待被路由给对端时、在Consumer Handler中对它们进行缓冲。可配置Message Queue来在单线程或多线程环境中高效而健壮地运行。当队列被实例化时,开发者可以选择所需的并发策略。在多线程环境中,Message Queue使用Monitor Object(管程对象)模式[1]来实现。

 

1.2.2 应用特有组件

 

  在图1-2中仅有两个组件(SupplierConsumer Handler)是特有于网关应用的。这些组件实现1.3.6中描述的非阻塞缓冲式I/ONon-blocking Buffered I/O)模式。SupplierConsumer Handler驻留在网关中,在那里它们被用作路由消息的初始源和目的地的代理;这些消息会被发送给在网络上的主机。这两种网关特有组件的行为概述如下:

 

Supplier HandlerSupplier Handler负责将到来的消息路由给它们的目的地。当Reactor在连接的通信端点上检测到事件时,它就会通知Supplier Handler。在Supplier Handler从该端点上接收完整的路由消息之后,它查询Routing Table,以确定消息的Consumer Handler目的地集。随后它请求选中的Consumer Handler转发消息给适当的对端目的地。

 

Consumer HandlerConsumer Handler负责将路由消息可靠地递送给它们的目的地。它实现了一种流控制机制,以缓冲由于暂时的网络拥塞或接收者缓冲区空间匮乏、不能立即发送而导致的路由消息的爆发。流控制是一种传输层机制,它确保源对端的数据发送速度不会超过目的对端缓冲和处理数据的能力。例如,如果目的对端的缓冲区空间耗尽,底层的TCP协议就会指示相关联网关的Consumer Handler停止发送消息,直到目的对端消耗了它现有的数据为止。

 

  网关通过定制、实例化及组合上面描述的ACE组件,集成了应用特有和应用无关的组件。如图1-3所示,SupplierConsumer Handler继承自共同的祖先:由ConnectorAcceptor生成的ACE Svc Handler类。每个Svc Handler都是一个远地相连对端的本地代理[2]。它包含的SOCK Stream使得对端可以通过已连接的Socket句柄交换消息。

 

 

1-3 ConsumerSupplier Handler继承层次

 

  Consumer Handler是根据非阻塞缓冲式I/O模式来实现的。因而,它使用ACE Message Queue来串连未发送的消息,其顺序为这些消息在流控制机制允许时所必须遵循的递送顺序。在被流控制的连接重又开放后,ACE构架通知它的Consumer Handler,后者就开始通过发送消息给对端来排空Message Queue

  为提高可靠性和性能,本论文中描述的网关利用了传输控制协议(TCP)。TCP为应用级网关提供了一种可靠、有序、非重复的字节流服务。尽管TCP连接天生是双向的,从对端发送给网关的数据使用了与从网关发送给对端的数据不同的连接。以这样的方式分离输入连接和输出连接有若干优点:

 

-它简化了网关Routing Table的构造;

-它允许更为灵活的连接配置和并发策略;

-它增强了可靠性,因为如果在一个连接上发生错误,SupplierConsumer Handler可独立地重新连接。

 

1.3 用于应用级网关的模式语言

 

  1.1描述了应用级网关的结构和功能。尽管这样的体系结构综述有助于澄清网关中关键组件的行为,它并没有揭示在这些软件组件之下的更深的关系和角色。特别是,它并没有给出网关为何以这种特定方式设计、或为何特定组件要以特定方式动作和交互的动机。了解这些关系和角色对于开发、维护和增强通信软件来说是至关紧要的。

  一种有效地捕捉和阐明这些关系和角色的方法是对生成它们的模式语言进行描述。研究在网关软件之下的模式语言提供了下面两种好处:

 

1.    确定常见设计挑战的成功解决方案。在网关体系结构之下的模式语言超出了应用的特定细节,并解决了通信软件开发者所面临的常见挑战。对这种模式语言的彻底了解使得开发者能够将网关软件体系结构广泛地复用于其他系统中,即使是在复用它的算法、实现、接口或详细设计不可行的时候[10]

2.    减少维护和增强网关软件的工作。模式语言有助于捕捉多个类和对象之间的协作,并说明其动机。这对于必须维护和增强网关的开发者来说是很重要的。尽管网关设计中的角色和关系已包含在源码中,将它们从周围的实现细节中提取出来可能会是昂贵而易错的。

 

1.3.1 战略模式

 

1-4 应用级网关模式语言

 

  图1-4演示下面五种战略模式,它们构成了生成面向连接的应用级网关的模式语言的一部分:

 

-反应堆(Reactor[1]该模式构造事件驱动应用,特别是服务器;它们并发地接收来自多个客户的请求,但依次对它们进行处理。

主动对象(Active Object[1]该模式使方法执行与方法调用去耦合,以增强并发、并简化对驻留在它们自己的线程控制中的对象的同步访问。

-组件配置器[1]该模式允许应用在运行时链接它的组件实现、或解除其链接,而不必修改、重编译或静态地重链接应用。它还可以在不关闭和重启运行中的进程的情况下,将组件重新配置进不同进程。

-接受器-连接器(Acceptor-Connector[1]该模式使网络系统中的连接建立及服务初始化与服务处理去耦合。

-非阻塞缓冲式I/ONon-blocking Buffered I/O)模式:该模式使输入机制与输出机制去耦合,以正确而可靠地路由数据,而又不会过度地阻塞应用处理。

 

  这五种模式之是战略性的,是因为它们显著地影响特定领域中的应用的软件体系结构。例如,当网关遇到拥塞或失败时,1.3.6描述的非阻塞缓冲式I/O模式能确保消息处理不会被中断或无限期地延迟。该模式有助于为使用可靠的面向连接的传输协议(比如TCP/IPIPX/SPX)的网关维持稳定的服务质量(QoS)。对于健壮、高效和可扩展的通信软件、比如应用级网关的开发来说,对本论文中描述的战略性通信模式的全面了解是必需的。

 

1.3.2 战术模式

 

  应用级网关实现还使用了许多战术模式,比如:

 

-适配器(Adapter[2]该模式将不兼容的接口转换为客户可以使用的接口。网关使用此模式来统一地处理不同类型的路由数据,比如命令、状态信息,以及大块数据。

-构建器(Builder[2]该模式为复杂对象的渐进构建提供工厂。网关使用此模式来通过配置文件创建它的Routing Table

-迭代器(Iterator[2]该模式使对容器的连续访问与容器的表示去耦合。网关使用此模式来将多个SupplierConsumer Handler连接到它们的对端,并对它们进行初始化。

-管程对象(Monitor Object[1]该模式对并发的方法执行进行同步,以确保在某一时刻只有一个方法在对象中运行。它还允许对象的多个方法相互协作地调度它们的执行序列。网关使用此模式来同步它的Message Queue的多线程配置。

-代理(Proxy[2]该模式提供了一种本地的代理对象,它代替一个远地对象进行工作。网关使用此模式来使主网关例行代码与(对端位于网络中其他主机上这一事实所导致的)延迟或错误屏蔽开来。

-模板方法(Template Method[2]该模式定义一种算法,其中的某些步骤是由派生类来提供的。网关使用此模式来有选择地重定义它的ConnectorAcceptor组件中的特定步骤,以使失败的连接能够被自动重启。

-包装外观(Wrapper Facade[1]该模式将现有非OO API所提供的功能和数据封装在更为简洁、健壮、可移植、可维护和内聚的OO类接口中。ACE构架使用此模式来提供一组OS无关的并发网络编程组件;网关使用了这些编程组件。

 

  比起前面概述的五种战略模式(它们是领域特有的并有着广泛的设计实现),这些战术模式是领域无关的,并只对软件设计有着相对局部的影响。例如,迭代器是一种战术模式,在网关中被用于顺序地处理Routing Table中的条目,而又不违反数据封装。尽管该模式是领域无关的,因而广泛地适用于许多应用,它所专事的问题并非像战略模式(比如非阻塞缓冲式I/O或反应堆)那样深入而普遍地影响应用级网关软件的设计。但是,全面了解战术模式对于实现高度灵活、能够有弹性地回应应用需求和平台环境变化的软件来说仍是必要的。

  这一部分的余下部分详细地描述各种战略模式,并解释它们是怎样被用于网关中的。

 

1.3.3 反应堆模式

 

意图:反应堆模式构造事件驱动的应用,特别是服务器;它们并发地接收来自多个客户的请求,但依次进行处理。

 

动机与压力:单线程应用必须处理来自多个来源的事件,而又不能无限期地阻塞在任何特定的来源上。下面的一些需要所带来的压力影响了单线程、事件驱动通信软件的设计:

 

 

1.    在单线程控制中高效地多路分离来自多个事件源的多种类型事件。在应用进程中来自多个来源的事件常常必须在事件多路分离一级被处理。通过在这一级处理事件,应用就有可能不再需要更为复杂的线程、同步,或锁定。

2.    扩展应用行为、而又无需改动事件分派构架。多路分离和分派机制常常是应用无关的,因而也就可以被复用。相反,事件处理器策略更加应用特有。通过分离这些事务,应用策略的改变可以不影响较低级的构架机制。

 

解决方案:应用反应堆模式、以同步地等待指示事件在一或多个事件源(比如已连接Socket句柄)上的到达。将多路分离和分派这些事件的机制集成到处理它们的服务中。使这些多路分离和分派机制与在这些服务中对指示事件进行的应用特有的处理去耦合。

 

结构、参与者和实现:1-5演示反应堆模式中的结构和参与者。Reactor定义的接口用于登记、移除和分派具体的事件处理器对象,比如网关中的SupplierConsumer Handler。该接口的实现提供了一组应用无关的机制。这些机制执行应用特有的事件处理器的多路分离和分派,以响应不同类型的输入、输出和定时器事件。

  Event Handler定义的抽象接口被Reactor用于分派回调方法;这些方法由登记了感兴趣的事件的对象定义。具体的事件处理器是继承自Event Handler的类,它有选择地重定义一些回调方法,以便通过应用特有的方式处理事件。

 

 

1-5 反应堆模式中的结构和参与者

 

动力特性:1-6演示反应堆模式中参与者之间的动力特性。这些动力特性可划分为下面两种模式(mode):

 

1.    初始化模式,在其中Concrete Event Handler对象被登记到Reactor

2.    事件处理模式,在其中Reactor调用已登记的对象上的upcall,后者随即以一种应用特有的方式对事件进行处理。

 

使用:在网关中Reactor被用于以下类型的事件分派操作:

 

1.    输入事件Reactor将每个到来的路由消息分派给与它的Socket句柄相关联的Supplier Handler,在这里消息被路由到适当的Consumer Handler。这种使用情况在图1-7中显示。

2.    输出事件。如1.3.61.3.7所描述的,Reactor确保外发的路由消息在已进行流控制的Consumer Handler之上被可靠地递送。

3.    连接完成事件Reactor分派连接完成事件,该事件指示被异步发起的连接的完成状态。1.3.5中描述的Connector组件使用了这些事件。

4.    连接请求事件Reactor还分派指示被动发起的连接的到达的事件。1.3.5中描述的Acceptor组件使用了这些事件。

 

 

1-6 反应堆模式的动力特性

 

 

1-7 在网关中使用反应堆模式

 

反应堆模式已被用于许多单线程事件驱动构架中,比如MotifInterviews[11]、系统V STREAMS[12]ACE OO通信构架[9],以及CORBA的一些实现[8]。此外,它还为下面介绍的所有其他的战略模式提供事件多路分离基础构造。

 

1.3.4 组件配置器模式

 

意图:组件配置器模式允许应用在运行时链接它的组件实现、或解除其链接,而不必修改、重编译或是静态重链接应用。它还可以在不关闭和重启运行中的进程的情况下,将组件重新配置进不同进程。

 

动机和压力:以下需求所带来的压力影响了高度灵活和可扩展通信软件的设计:

 

1.    推迟选择组件的特定实现、直至设计周期中非常迟后的阶段。推迟这些配置决策、直至安装时或运行时,显著地增加了开发者可用的设计选择。例如,可通过运行时上下文信息来指导实现决策,也可动态地将组件(重)配置进应用中。

2.    通过编写或构造多个独立开发的组件来构建完整应用。应用的许多重复发生的组件配置和初始化行为应被分解进可复用方法中。这样的事务分离允许在运行时将新版本组件链接进应用中,而不用中断目前正在执行的组件。

 

解决方案:应用组件配置器模式、以使组件接口与它们的实现去耦合,并使应用不依赖组件实现被配置进应用进程的时间点。

 

结构、参与者和实现:1-8演示组件配置器模式的结构和参与者。针对其事件多路分离和分派需要,该模式复用了反应堆模式的ReactorEvent HandlerComponentEvent Handler的子类,并增加了在C++对象被动态链接和解除链接时、用于初始化和终止它们的接口。应用特有的组件继承自Component,并有选择地重定义它的initfini方法,以分别实现定制的初始化和终止行为。

Component Repository记录哪些Component正在进行链接和活动。Component Config是一种外观(fa?ade),它将其他组件的行为改编为“交响乐” [2],并为运行时组件的链接、启用、挂起、恢复和解除链接提供单一访问点。

 

 

1-8 组件配置器模式中的结构和参与者

 

 

1-9 组件配置器模式的动力特性

 

动力特性:1-9演示组件配置器模式中的参与者之间的动力特性。这些动力特性可被划分为以下两种模式(mode):

 

1.    配置模式,动态地将Component链接到应用,或从应用中解除其链接。

2.    事件处理模式,使用像反应堆或主动对象[1]这样的模式来处理到来的事件。

 

使用:在如图1-10所示的网关中使用了组件配置器模式。Reactive Gateway组件是网关的一种单线程实现,它可通过配置脚本中的命令而被动态链接。要通过多线程实现来动态地替代这一组件,Component Config只需重新查询它的comp.conf文件,解除Reactive Gateway的链接,动态地链接Thread-per-Connection GatewayThread Pool Gateway,并初始化该新实现。Component Config外观使用动态链接来高效地实现组件配置器模式。

 

 

1-10 在网关中使用组件配置器模式

 

组件配置器模式被用于Windows NT服务控制管理器(SCM)中,它允许主SCM进程自动发起并控制管理员安装的服务。通常,现代操作系统(比如SolarisLinuxWindows NT)为动态配置的内核级服务驱动程序(它们实现组件配置器模式)提供了支持。组件配置器模式的另一种使用是Java中的applet机制,它支持Java applet的动态下载、初始化、启动、停止,以及终止。

 

1.3.5 接受器-连接器模式

 

意图:接受器-连接器模式使网络系统中的连接建立及服务初始化与服务处理去耦合。

 

动机和压力:面向连接的应用(比如我们的应用级网关)和中间件(比如CORBA)常常使用像scoket[13]这样的较低级的网络编程接口来编写。以下需求所带来的压力影响了使用这些较低级接口编写的服务的初始化:

 

1.    将连接建立代码复用于每个新服务。服务的关键特性,比如通信协议或数据格式,应该能够独立于用于建立连接的机制而透明地发展。因为服务特性的变动比连接建立机制更为频繁,分离这些事务有助于降低软件的耦合、并增加代码复用。

2.    使连接建立代码可跨越含有不同网络编程接口的平台而移植。为接受连接和执行服务而对接受器-连接器的机制进行参数化有助于通过整个地替换这些机制来改善可移植性。这使得连接建立代码可跨越含有不同网络编程接口的平台进行移植,比如有Socket、但没有TLI,或者反之亦然。

3.    使用灵活的服务并发策略。在连接建立后,对端应用使用此连接来交换数据,以执行某种类型的服务,比如远地登录或HTML文档传输。服务可以运行在单线程中、在多线程中,或多进程中,而不管连接是如何建立的,或如何被初始化的。

4.    确保被动模式的I/O句柄不会被偶然用于读写数据。通过彻底使连接建立逻辑与服务处理逻辑去耦合,被动模式的Socket端点不可能被不正确地使用(例如,试图在用于接受连接的被动模式侦听者端口上读写数据)。这消除了一类重要的网络编程错误。

5.    高效地主动与大量对端建立连接。当应用必须在长延迟WAN上与大量对端高效地建立连接时,可能必须使用异步操作,并在非阻塞模式中发起和完成多个连接。

 

解决方案:应用接受器-连接器模式来使网络应用中的对端服务的连接及初始化与在它们被连接和初始化后所执行的服务处理去耦合。

 

结构、参与者和实现:1-11演示接受器-连接器模式中的参与者的分层结构。AcceptorConnector组件是对连接和启用Svc Handler所需的资源进行装配的工厂。Svc Handler是与相连对端交换消息的组件。

 

 

1-11 接受器-连接器模式中的结构和参与者

 

接受器-连接器的连接层中的参与者有效地利用了反应堆模式。例如,在Reactor通知Connector、一个先前发起的连接到对端的请求已经完成后,Connector的异步初始化策略就会建立起一个连接。反应堆模式的使用使得多个Svc Handler能在单线程控制中被异步地初始化。

为增加灵活性,AcceptorConnector组件可通过特定类型的IPC机制和SVC HANDLER来参数化。IPC机制提供底层传输机制(比如SocketTLIC++包装外观)来用于建立连接;而SVC HANDLER定义的抽象接口用于定义与相连对端进行通信的服务。Svc Handler可通过PEER STREAM端点来参数化。当连接建立时,AcceptorConnector组件将此端点与它的对端关联起来。

通过从Event Handler继承,Svc Handler可以登记到Reactor,并使用反应堆模式来在与AcceptorConnector相同的线程控制中处理它的I/O事件。反过来,Svc Handler也可以使用主动对象模式,在单独的线程中处理它的I/O事件。1.3.7评估了在这两种模式之间进行的折衷。

参数化类型被用于使接受器-连接器模式的连接建立策略与服务类型及连接机制类型去耦合。开发者为这些类型提供模板参数,以生成应用层AcceptorConnector,比如由网关用于初始化SupplierConsumer HandlerConnector。这样的设计使得开发者可以整个地替换SVC HANDLERIPC机制,而不会影响接受器-连接器模式的初始化策略。

注意通过使用[2]中描述的抽象工厂(Abstract Factory)或工厂方法(Factory Method)模式,经由继承和动态绑定可以达到类似程度的去耦合。之所以使用参数化类型来实现此模式,是因为它们改善了运行时效率。一般而言,模板为改善运行时性能而在编译及链接时开销与空间开销之间进行折衷。

 

 

1-12 Acceptor组件的动力特性

 

动力特性:1-12演示该模式的Acceptor组件的参与者之间的动力特性。这些动力特性被划分进以下三个阶段:

 

1.    端点初始化阶段,创建由PEER ACCEPTOR封装的被动模式端点,这个PEER ACCEPTOR被绑定到一个网络地址,比如IP地址和端口号。该被动模式端点侦听来自对端的连接请求。它被登记到Reactor,后者驱动事件循环,在该端点上等待连接请求从对端到达。

2.    服务启用阶段。因为Acceptor继承自Event Handler,当连接请求事件到达时,Reactor可以分派Acceptorhandle_event方法。该方法执行AcceptorSvc Handler初始化策略:(1)装配创建新Concrete Svc Handler对象所需的资源,(2)将连接接受进该对象,以及(3)通过调用Svc Handleropen挂钩方法将其启用。

3.    服务处理阶段。在Svc Handler被启用后,它处理在PEER STREAM上到来的事件消息。Svc Handler可以使用像反应堆或主动对象[1]这样的模式来处理到来的事件消息。

 

在该模式的Connector组件的参与者之间的动力特性可被划分进下面三个阶段:

 

1.    连接发起阶段,主动地连接一或多个Svc Handler到它们的对端。连接可被同步或异步地发起。Connectorconnect方法实现主动建立连接的策略。

2.    服务初始化阶段,当Svc Handler的连接成功完成时,通过调用它的open方法来将其启用。Svc Handleropen方法随即执行服务特有的初始化。

3.    服务处理阶段,使用在Svc Handler和与其相连的对端之间交换的数据来执行应用特有的服务处理。

 

1-13使用异步连接建立来演示动力特性的这三个阶段。注意Connector的连接发起阶段是怎样暂时与服务初始化阶段分离的。这样的设计使得多个连接发起可在单线程控制中并行地进行。同步连接建立的动力特性也是类似的。在此例中,Connector将连接发起和服务初始化阶段组合进单个阻塞操作中。

 

 

1-13 异步Connector组件的动力特性

 

一般而言,同步的连接建立对于以下情况来说是有用的:

 

-如果建立连接的响应延迟非常低,比如通过回路(loopback)设备与在同一主机上的服务器建立连接。

-如果有多个线程控制可用,并且,使用不同线程来同步地连接每个Svc Handler是可行的。

-如果不到连接已建立,客户应用无法执行有用的工作。

 

相反,异步的连接建立对于以下情况来说是有用的:

 

-如果连接响应延迟很高,而又有许多对端要连接,例如,在高响应延迟的WAN上建立大量连接。

-如果只有单个线程控制可用,例如,如果OS平台不提供应用级线程。

-如果客户应用必须在连接正在被建立的同时执行另外的工作,比如刷新GUI

 

情况常常是,网络服务(比如我们的应用级网关)必须在不知道它们将同步还是异步地进行连接的情况下开发。因此,一个通用网络编程构架所提供的组件必须支持多种同步和异步的使用情况。

接受器-连接器模式通过使连接建立逻辑与服务处理逻辑相分离,增加了网络构架组件的灵活性和复用。在(1AcceptorConnector组件与(2Svc Handler之间的唯一耦合发生在服务初始化阶段,在Svc Handleropen方法被调用时。在这时,Svc Handler可以使用任何合适的应用级协议或并发策略来执行它的服务特有的处理。

例如,当消息到达网关时,Reactor可用于分派Supplier Handler,以划分消息帧、确定外发路由,并递送消息给它们的Consumer Handler。但是,Consumer Handler可以使用不同类型的并发机制(比如1.3.7描述的主动对象)来发送数据给远地的目的地。

 

使用:1-14演示接受器-连接器模式的Acceptor组件是怎样在网关扮演被动连接角色时被使用的。在此例中,对端连接到网关,后者使用Acceptor来使SupplierConsumer Handler的被动初始化与处理器被初始化后所执行的路由任务去耦合。

 

 

1-14 在网关中使用Acceptor组件

 

1-15演示接受器-连接器模式的Connector组件怎样被网关用于简化连接大量对端的任务。在此例中,对端地址在网关初始化的过程中从配置文件中读取。网关使用构建器(Builder)模式[2]来将这些地址绑定到动态分配的Consumer HandlerSupplier Handler。因为这些处理器继承自Svc Handler,可以使用迭代器模式[2]来异步地发起所有连接。随后就使用Connector来并行地完成连接。

 

 

1-15 在网关中使用Connector组件

 

1-15显示Connector在四个连接被建立后的状态。其他三个还未完成的连接由Connetor拥有。如该图中所示,Connector维护连接还暂未完成的三个Handler的表。在连接完成时,Connector将每个已连接的Channel从它的表中移除,并将其启用。在单线程实现中,Supplier Handler在它们被启用后将自身登记到Reactor。自此时起,当路由消息到达时,Supplier Handler接收它们、并转发给Consumer Handler,后者再将这些消息递送到它们的目的地(这些活动在1.3.6中描述)。

除了建立连接,网关还可与ConnectorReactor联合使用、以确保连接在网络错误发生时重启。通过确保信道在意外断开时(例如,如果对端崩溃,或由于网络拥塞、大量的数据在Consumer Handler处排队等待)自动地重新发起,增强了网关的容错性。如果连接意外失败,通过使用Reactor的定时器分派能力,一种指数后退算法可以高效地重启连接。

在像inentd[13]listen[14]这样的网络服务器管理工具中,可以发现接受器-连接器模式的意图和综合体系结构。这些工具利用主接受器进程,在一组通信端口上侦听连接。每个端口被关联到一个与通信有关的服务(比如标准的Internet服务ftptelnetdaytimeecho)。当服务请求到达被监控的端口时,接受器进程接受该请求,并分派适当的预登记处理器来执行服务。

 

1.3.6 非阻塞缓冲式I/O模式

 

意图:非阻塞缓冲式I/O模式使输入机制与输出机制去耦合,从而使数据能被正确而可靠地路由,而不会不适当地阻塞应用处理。

 

动机与压力:当在到来或外发的网络连接上发生拥塞或失败时,不能中断或无限期地延后网关中的消息路由。因而,在构建健壮的面向连接网关时,必须消除以下需求所带来的压力:

 

1.    防止行为不端的连接破坏行为良好的连接的QoS。输入连接可能会因为对端断开而失败。同样地,作为网络拥塞的结果,输出连接也可能进行流控制。在这些类型的情况下,网关不能在任何单个连接上执行阻塞式sendrecv操作,因为(1)整个网关可能会无限期挂起,或是(2)在其他连接上的消息无法发送或接收,提供给对端的QoS将下降。

2.    允许为处理输入和输出使用不同的并发策略,包括(1)使用反应堆模式(1.3.3)的单线程处理,以及(2)使用主动对象模式(1.3.7)的多线程处理。取决于各种因素,比如CPU数、上下文切换开销,以及对端数,各种策略适用于各不相同的情况。

 

解决方案:应用非阻塞缓冲式I/O模式、使输入处理与输出处理去耦合,以防止阻塞,并允许将定制的并发策略灵活地配置进应用中。

 

结构、参与者和实现:1-16演示非阻塞缓冲式I/O模式中的参与者的层次结构。I/O层为Supplier Handler提供事件源,为Consumer Handler提供事件槽。Supplier Handler使用Routing Table来将路由消息映射到一或多个Consumer Handler。如果消息不能立即被递送到它们的目的地,就会缓冲在Message Queue中,以在后面进行传输。

 

 

1-16 非阻塞缓冲式I/O模式中的结构和参与者

 

因为Supplier HandlerConsumer Handler是去耦合的,它们的实现可以独立地变化。这样的事务分离十分重要,因为它允许为输入和输出使用不同的并发策略。这样的去耦合的效果在1.3.7中进一步讨论。

 

动力特性:1-17演示非阻塞缓冲式I/O模式中的参与者之间的动力特性。这些动力特性可被划分进三个阶段:

 

1.    输入处理阶段Supplier Handler在其中将到来的TCP片段重新装配进完整的路由消息,而又不阻塞应用进程。

2.    路由选择阶段。在重新装配了完整的消息后,Supplier Handler查询Routing Table,以选择负责发送路由消息给它们的对端目的地的Consumer Handler

3.    输出处理阶段,所选择的Consumer Handler在其中传输路由消息给它们的目的地,而又不阻塞应用进程。

 

 

1-17 非阻塞缓冲式I/O模式的动力特性

 

使用:在本论文中的其他战略模式棗也就是,反应堆、连接器、接受器,以及主动对象棗可被应用于许多类型的通信软件。相反,非阻塞缓冲式I/O模式更紧密地与在对端间路由消息的网关风格的应用耦合在一起。构建可靠的面向连接的网关的主要挑战集中在避免阻塞式I/O上。网关需要可靠地对发生在连接上的流控制进行管理;这些连接被Consumer Handler用于转发消息给对端。如果网关在拥塞的连接上进行发送时无限期地阻塞,到来的消息就无法被路由,即使其他消息是去往未进行流控制的Consumer Handler

1.3.6的余下部分描述怎样在网关的单线程反应式版本中实现非阻塞缓冲式I/O模式(1.3.7检查非阻塞缓冲式I/O模式的多线程主动对象版本)。在此实现中,非阻塞缓冲式I/O模式使用Reactor来作为网关I/O操作的协作式多任务调度器;这些操作在单线程中的不同连接上进行。使用单线程可消除以下开销:

 

同步:例如,对像Routing Table这样的共享对象的访问不需要序列化;以及

上下文切换:例如,所有消息路由都可在单个线程中发生。

 

在非阻塞缓冲式I/O模式的反应式实现中,Supplier HandlerConsumer HandlerEvent Handler的后代。这样的层次化继承设计使得网关可以在消息到达和流控制情况平息时、通过让Reactor分别分派SupplierConsumer Handlerhandle_event方法来路由消息。

使用反应堆模式来实现非阻塞缓冲式I/O模式涉及以下步骤:

 

1.    初始化非阻塞式端点。在SupplierConsumer HandlerAcceptorConnector启用后,它们的句柄被设置进非阻塞模式。非阻塞式I/O的使用对于避免在拥塞的网络链接上发生阻塞来说是必要的。

2.    输入消息的重新装配和路由Supplier Handler以片段的方式接收路由消息。如果整个消息不是立即可用,Supplier Handler必须缓冲这些片段,并将控制返回给事件循环。这对于防止Supplier Channel上的“head of line”阻塞来说是必要的。当Supplier Channel成功地接收了整个消息、并划分出消息帧时,它使用Routing Table来确定适当的递送该消息的Consumer Handler集。

3.    消息递送。所选择的Consumer Handler尝试发送消息给目的对端。消息必须以“先进先出”(FIFO)顺序可靠地递送。为避免阻塞,所有Consumer Handler中的send操作必须进行检查,以确定网络链接没有进行流控制。如果没有进行流控制,消息才能够被成功发送。图1-18右上角的Consumer Handler描述了这一情况。但是如果链接已进行流控制,非阻塞缓冲式I/O模式实现必须使用不同的策略。图1-18右手较低的Consumer Handler描述了这一情况。

 

 

1-18 在单线程反应式网关中使用非阻塞缓冲式I/O模式

 

为处理已进行流控制的连接,Consumer Handler将它正试图发送的消息插入它的Message Queue中。它随即指示Reactor在流控制情况减轻时回调Consumer Handler;并返回主事件循环。在可以再次尝试发送时,Reactor分派Consumer Handlerhandle_event方法,后者就再次尝试该操作。这一系列步骤可能重复多次,直到整个消息被成功地传送。

注意在每次I/O操作后,网关总是立即将控制返回给它的主事件循环,而不管它是否发送或接收了整个消息。这是非阻塞缓冲式I/O模式的本质棗它将消息正确地路由给对端,而不阻塞在任何单个I/O信道上。

 

1.3.7 主动对象模式

 

意图:主动对象模式使方法执行与方法调用去耦合,以增强并发、并简化对驻留在它们自己的线程控制中的对象的同步访问。

 

动机和压力:1.3.6中的单线程网关所用的所有战略模式的层次都在反应堆模式之上构建。接受器-连接器和非阻塞缓冲式I/O模式都使用反应堆来作为在单线程控制中初始化和路由消息的调度器/分派器。一般而言,反应堆模式构成了单线程反应式系统中的中央事件循环。例如,在单线程网关实现中,Reactor提供了并发控制的粗粒度形式,对在进程中的事件多路分离和分派层上的事件处理器的调用进行序列化。这消除了在网关中对额外的同步机制的需要,并使上下文切换开销得以最小化。

反应堆模式良好地适用于使用短持续时间回调的应用,比如接受器模式中的被动连接建立。但是,对于长持续时间操作,比如在网络拥塞期间在已进行流控制的Consumer Handler上的阻塞,这种模式并不十分适用。事实上,非阻塞缓冲式I/O模式实现中的许多复杂性都源于使用反应堆模式来作为协作式多任务机制。一般而言,该模式不足以消除下面的需求所带来的压力:

 

 

解决方案:应用主动对象模式来使在对象上的方法调用与方法执行去耦合。方法调用应该在客户的线程控制中发生,而方法执行应该在另外的线程中发生。此外,所提高的接口应该使客户线程看起来像是在调用普通的方法。

 

结构、参与者和实现:1-19演示主动对象模式中的结构和参与者。Proxy将主动对象的公共方法输出给客户。Scheduler基于同步和调度约束来确定下一个要执行的方法。Activation List维护待处理Method Request的队列。Scheduler确定这些Method Request被执行的顺序(在网关中使用了FIFO调度器来维护消息递送的顺序)。Servant维护实现方法所共享的状态。

 

 

1-19 主动对象模式中的结构和参与者

 

动力特性:1-20演示主动对象模式中的参与者之间的动力特性。这些动力特性被划分进下面的三个阶段:

 

1.    方法请求构造。在此阶段,客户应用调用Proxy定义的方法,从而触发Method Request的创建;Method Request维护绑定到方法的参数,以及任何其他的方法执行和返回结果所需的绑定。一个到Future(期货)对象的绑定被返回给方法的调用者。

2.    调度/执行。在此阶段Scheduler获取一个互斥锁,查询Activation Queue来确定哪些Method Request满足同步约束。Method Request随即被绑定到当前的Servant,并可以访问/更新该Servant的状态。

3.    返回结果。最后的阶段将Method Request的结果绑定到Future[15]对象,后者在方法结束执行时将返回值返回给调用者。Future是一种同步对象,它强制实现“一次写,多次读”同步。随后,任何与此Future对象会合(rendezvous)的读者都将对期货进行估算并获取结果值。当FutureMethod Request不再被需要时,它们可被当作垃圾回收。

 

 

1-20 主动对象模式的动力特性

 

使用:1.3.6中描述的网关实现是单线程的。它使用非阻塞缓冲式I/O模式的反应堆模式实现来作为协作式多任务调度器,以分派网关感兴趣的事件。在我们实现了许多单线程网关之后,下面的事实变得清楚起来:使用反应堆模式来作为所有网关路由I/O操作的基础是易错和难以维护的。例如,开发者很难记住为什么在不能进行I/O操作控制时、必须将控制迅速地返回给Reactor的事件循环。在单线程网关中,这样的误解变成了错误的常见来源。

为避免这些问题,有许多多线程网关使用了主动对象模式的各种变种来构建。该模式允许Consumer Handler在发送消息给对端时独立地阻塞。这一部分的余下部分描述Consumer Handler怎样使用主动对象模式来成为多线程的。这样的修改充分简化了非阻塞缓冲式I/O模式的实现,因为Consumer Handler可以阻塞在它们自己的主动对象线程中,而不会影响其他的Handler。将Consumer Handler实现为主动对象还消除了在使用Reactor调度Consumer Handler时所需的微妙而易错的协作式多任务编程技术。

1-21演示非阻塞缓冲式I/O模式的主动对象版本。注意与图1-18中的反应堆方案相比,它有多么的简单。之所以会这样,是因为复杂的输出调度逻辑被移进了主动对象,而不是成为应用开发者的责任。

 

 

1-21 在多线程主动对象网关中使用非阻塞缓冲式I/O模式

 

通过检查在产品网关系统中实现非阻塞缓冲式I/O模式的源码,还可以观察到单线程和多线程网关之间的复杂性差异。由于所有围绕着实现的错误处理代码和协议特有的细节,很难通过检查源码来简单地确定这些复杂性的原因。这些细节倾向于掩盖关键的洞见:在单线程和多线程方案之间的复杂性的主要差异来自于选择了反应堆模式,而不是主动对象模式

本论文明确地探讨反应堆和主动对象模式之间的交互和权衡,从而揭示不同设计选择的效果。一般而言,为紧密相关的模式之间的交互和关系编写文档是有挑战性的、未解决的课题;模式社群正在致力于这一课题的解决。

 

1.4 相关模式

 

[2, 16, 1]标识、命名、分类许多基本的的体系结构和设计模式。这一部分检查本论文中描述的模式与文献中的其他模式的关联。注意1.3.2中概述的许多战术模式构成了实现本论文中介绍的战略模式的基础。

反应堆模式与观察者(Observer)模式[2]有关。在观察者模式中,当一个主题变动时,多个相关对象会被自动更新。在反应堆模式中,当一个事件发生时,会自动分派一个处理器。因而,尽管事件源可能有多个,反应堆为每个事件分派单个的处理器。反应堆模式还提供了一个外观[2]。外观模式给出一种接口,使应用与子系统中复杂的关系屏蔽开来。反应堆模式使应用与执行事件多路分离和事件处理器分派的复杂机制屏蔽开来。

组件配置器模式与构建器(Builder)和中介者(Mediator)模式[2]有关。构建器模式提供的工厂用于渐进地构造复杂对象。中介者协调在它的关联者之间的交互。组件配置器模式提供的工厂用于在运行时将组件配置和初始化进应用中。在运行时,组件配置器模式允许应用所提供的组件被渐进地修改,而不用中断执行中的组件。此外,组件配置器模式还在运行时对被配置进应用的组件与想要更新、挂起、恢复或移除组件的外部管理员之间的交互进行协调。

接受器-连接器模式与模板方法(Template Method)、策略(Strategy)以及工厂方法(Factory Method)模式[2]有关。在模板方法模式中,所编写的算法的某些步骤由派生类来提供。在工厂方法模式中,子类中的方法创建一个伙伴来执行一项特定的任务,但此任务并没有与用于创建该任务的协议耦合在一起。接受器-连接器模式中的AcceptorConnector组件是使用模板方法或策略来创建、连接并启用通信信道的处理器的工厂。接受器-连接器模式的意图与[16]中描述的客户/分派器/服务器模式是类似的。它们都关心主动连接建立与后续服务的分离。主要的区别是接受器-连接器模式同时致力于被动/主动及同步/异步的连接建立。

非阻塞缓冲式I/O模式与中介者模式有关,后者使软件系统的协作组件去耦合,并允许它们相互交互,而又没有直接的相互依赖。非阻塞缓冲式I/O模式专用于消除与网络通信相关的压力。它使用于处理输入消息的机制与用于处理输出消息的机制去耦合,以防止阻塞的发生。此外,非阻塞缓冲式I/O模式还允许为输入和输出信道使用不同的并发策略。

 

1.5 结束语

 

通过本论文阐释的模式语言,开发者可以在产品通信网关中广泛地复用设计专家经验和软件组件。该语言中的模式阐明了执行核心通信软件任务的对象的结构以及它们之间的协作。这些模式所关注的任务包括事件多路分离和事件处理器分派,应用服务的连接建立和初始化、并发控制,以及路由。

本论文中描述的模式语言和ACE构架组件已被作者和他的同事复用于许多产品通信软件系统中,范围从电信、电子医学成像及航空控制项目[10, 5, 7]到学院研究项目[9, 8]。总而言之,通过在高于(1)源码和(2)聚焦于个体对象和类的OO设计模式的水平上捕捉软件体系结构中的参与者的结构和动力特性,该模式语言帮助了这些系统中的组件和构架的开发。

对我们使用模式所得到的经验和教训的深入讨论见[4]。基于ACE、演示本论文中所有模式的单线程和多线程网关的例子可在http://www.cs.wustl.edu/~schmidt/ACE.html自由获取。

 

参考文献

 

[1] D. C. Schmidt, M. Stal, H. Rohnert, and F. Buschmann, Pattern-Oriented Software Architecture: Patterns for Concurrency and Distributed Objects, Volume 2. New York, NY: Wiley & Sons, 2000.

[2] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995.

[3] J. O. Coplien and D. C. Schmidt, eds., Pattern Languages of Program Design. Reading, MA: Addison-Wesley, 1995.

[4] D. C. Schmidt, “Experience Using Design Patterns to Develop Reuseable Object-Oriented Communication Software,” Communications of the ACM (Special Issue on Object-Oriented Experiences), vol. 38, October 1995.

[5] I. Pyarali, T. H. Harrison, and D. C. Schmidt, “Design and Performance of an Object-Oriented Framework for High-Performance Electronic Medical Imaging,” in Proceedings of the 2nd Conference on Object-Oriented Technologies and Systems, (Toronto, Canada), USENIX, June 1996.

[6] D. C. Schmidt and T. Suda, “Measuring the Performance of Parallel Message-based Process Architectures,” in Proceedings of the Conference on Computer Communications (INFOCOM), (Boston, MA), pp. 624–633, IEEE, April 1995.

[7] T. H. Harrison, D. L. Levine, and D. C. Schmidt, “The Design and Performance of a Real-time CORBA Event Service,” in Proceedings of OOPSLA ’97, (Atlanta, GA), ACM, October 1997.

[8] D. C. Schmidt and C. Cleeland, “Applying a Pattern Language to Develop Extensible ORB Middleware,” in Design Patterns in Communications (L. Rising, ed.), Cambridge University Press, 2000.

[9] D. C. Schmidt, “Applying Design Patterns and Frameworks to Develop Object-Oriented Communication Software,” in Handbook of Programming Languages (P. Salus, ed.), MacMillan Computer Publishing, 1997.

[10] D. C. Schmidt and P. Stephenson, “Experiences Using Design Patterns to Evolve System Software Across Diverse OS Platforms,” in Proceedings of the 9th European Conference on Object-Oriented Programming, (Aarhus, Denmark), ACM, August 1995.

[11] M. A. Linton, J. Vlissides, and P. Calder, “Composing User Interfaces with InterViews,” IEEE Computer, vol. 22, pp. 8–22, February 1989.

[12] D. Ritchie, “A Stream Input–Output System,” AT&T Bell Labs Technical Journal, vol. 63, pp. 311–324, Oct. 1984.

[13] W. R. Stevens, UNIX Network Programming, First Edition. Englewood Cliffs, NJ: Prentice Hall, 1990.

[14] S. Rago, UNIX System V Network Programming. Reading, MA: Addison-Wesley, 1993.

[15] R. H. Halstead, Jr., “Multilisp: A Language for Concurrent Symbolic Computation,” ACMTrans. Programming Languages and Systems, vol. 7, pp. 501–538, Oct. 1985.

[16] F. Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, and M. Stal, Pattern-Oriented Software Architecture - A System of Patterns. Wiley and Sons, 1996.

 


This file is decompiled by an unregistered version of ChmDecompiler.
Regsitered version does not show this message.
You can download ChmDecompiler at : http://www.zipghost.com/