POSA2 第一章 并发网络对象(1.2并发网络软件的难题)

来源:互联网 发布:mac与pc的区别 编辑:程序博客网 时间:2024/05/05 04:56

理论上,开发使用了并发网络服务的软件应用程序能够提高系统性能,可靠性,可扩展性和成本效益。而实际上,由于单机应用程序和网络应用程序在架构上的关键区别,开发高效,健壮,可扩展,负担得起的并发网络应用程序是困难的。

在单机应用程序架构里,用户界面,应用服务处理和永久数据资源都在同一台计算机上,外围设备也直接连接在上边。相反,在网络应用程序架构里,交互表示(interactivepresentation),应用服务处理和数据资源可能在多个松散耦合的主机上,并且服务层(译注:应用服务处理)通过局域网或者广域网络连在一起。

一个网络系统架构的例子是X-terminals和“thin client NetPCs”。在这种环境中, UI表示层通过显示服务实现在最终用户的主机上。而处理能力由运行全部或部分应用服务的主机(们)提供。永久资源的访问由一个或者多个网络文件服务器协调。其它服务,如名字和目录服务,时间服务,HTTP服务器,以及缓冲和网络管理服务也可以运行在网络中,并为应用程序提供额外的能力。

1

有三种常见的原因采纳网络架构:

l       协作和连接。Web和电子商务的爆炸式增长例证了使用网络的大部分常见原因中的一个:连接并访问大量在地理上分布的信息和服务的能力。实时消息软件和“聊天室”在Internet上的流行强调了另一个使用网络的动机:保持与家人,朋友,合作者和客户的联系。

l       增强性能,可扩充性和容错能力。基于网络的架构的性能和可扩充性可以通过利用网络的并行处理能力得到增强。例如,多个计算和通信服务处理任务可以在不同的主机上并行的运行。类似的,不同的应用服务能够在多个主机上被复制。复制能够防止单点失败,以此来增加系统针对部分失败的可靠性。

l       成本效益。网络架构产生出分离的模块化的应用程序,这些程序可以共享昂贵的外设,如大容量文件服务器和高解决方案的打印机。类似的,被选择的应用程序组件和服务能够被委派运行在专门的处理属性的主机上,如具有高性能磁盘控制器,大量内存或者增强的浮点性能的主机。

尽管网络应用程序呈现出很多潜在的好处,但是它们比单机应用程序更难设计,实现,调试,优化和管理。例如,开发者必须解决在单机应用程序中无关或者没有疑问的话题。这些话题包括:

l       连接的建立和服务初始化

l       事件多路分离和事件处理者分派

l       主要和次要存储管理和缓冲

l       静态和动态组件配置

l       并发和同步

这些话题通常独立于特别的应用需求,因此学会掌握它们能够帮助解决很大范围内的软件开发问题。而且,在这些话题的情境中,很多设计和编程的难题是由于并发网络系统的几个固有的和随机的复杂性:

l       与并发网络系统相关的固有复杂性通常包括管理带宽[ZBS97]最小化延迟时间()[SC99]和延迟变化()[PRS+99],局部失败的检测和恢复[CRSS+98],恰当的服务分割决定和负载均衡策略[IEEE95][BR94]。类似的,在并发编程时常见的固有的复杂性包括消除竞争条件并避免死锁[Lea99a],决定合适的线程调度策略[SKT96],以及优化远端系统协议处理性能。

l       与并发网络系统相关的随机复杂性通常包括缺乏方便的操作系统API[Sch97],不足的调试支持和缺乏对并发网络应用程序的分析工具[LT93],大量使用基于算法的分解而不是面向对象的分解[Boo94],以及不断发明和创造核心概念和通用组件[Kar95]

因此,我们会在本节讨论与建造并发网络系统相关的很多设计和编程难题。但是本书中的模式不会与并发网络相关的所有方面有关。因此,第6章“将模式组织在一起”,将本书中的模式与其它著作中相关方面的模式联系起来。剩下的难题是未来模式研究中悬而未决的问题,在第7章“模式的过去,现在和未来”中描述。

难题1:服务访问和配置

在单机应用程序,组件间可以通过调用函数并传递参数以及访问全局变量的方式来合作。相反,在网络应用程序中,组件间通过以下方式合作:

l       进程间通信(IPC)机制,例如共享内存,管道,以及Socket[Set98]Socket可以基于TCPUDPIP[Ste93]或者ATM[CFFT97]等网络协议。

l       通信协议[Set93],如TELNETFTPSMTPHTTP,以及LDAPLDAP被很多类型的服务使用,如远程登录,文件传输,电子邮件,Web内容传递,以及分布式目录,用来为应用程序提供内聚的组件和功能。

l       通过COM+[Box97]CORBA[OMG98c]这种高级的通信中间件通过应用程序级的服务进行远程操作。

应用程序和软件组件可以通过网络系统中各层定义的API访问这些通信机制。

 

2

设计高效的API对访问这些通信机制是很重要的,因为这是被应用程序,组件和服务开发者直接使用的编程接口。

对于基础网络或系统程序,如TELNETFTP,服务访问通常导致调用:

l       并发服务访问API,如UNIX进程[Ste99]POSIX线程[IEEE96],或者Win32线程[Sol98]来管理并发

l       IPC服务访问API,如UNIXInternetSocket[Ste98],来配置连接以及分别与位于同一台主机或者位于不同主机上的进程进行通信。

一些随机复杂性来自于使用底层操作系统C API来访问网络和主机服务时:

l       过多的底层细节。使用操作系统API创建网络应用程序需要开发者了解很多与底层细节紧密相关的知识。例如:开发者必须小心处理每个系统调用的错误码,并在应用程序代码中自己解决这些问题。例如,使用wait()系统调用的UNIX服务器开发者必须区分没有子进程时返回的错误码和被信号中断时返回的错误码。在后一种情况下,wait()必须被重新调用一遍。强迫应用程序开发者处理这些细节会转移他们在更多策略问题上的注意力,如服务器的语义和软件架构。

l       不断重新发明和创造不兼容的更高级编程抽象(higher-level programming abstractions)。一个处理过多底层操作系统API细节的常用方法是定义高级编程抽象。例如,很多Web服务器通过创建一个文件缓冲组件来避免处理每个客户端请求时都访问文件系统[HPS99]。然而,这类抽象经常被不相关的开发者或团队重新发明创造出来。这种特殊的软件开发过程将应用程序开发者的精力从满足用户需求上转移开,而且确实会阻碍生产力。它也会产生过多的不兼容的组件,这些组件没有充分的文档和调试,因此不容易跨项目复用。

l       高错误隐患。直接使用操作系统API进行编程令人厌烦并容易出错,因为这些API缺少类型安全并且很微妙。例如,很多网络应用程序使用SocketAPI[MBKQ96]进行编写,这些API是用C语言定义的。然而,socket被描述为无类型的句柄。这些句柄增加了编程错误和运行期错误的可能性[Sch92]。特别是操作可以被错误的执行,例如在一个被动模式的只支持建立连接的句柄上执行数据传输操作。

l       不方便。操作系统API非常不方便,甚至不能兼容同一个平台的不同发布版本。例如,Win32平台的Socket API的实现(WinSock)与UNIX平台有细微的不同。因此高级Socket操作,如多播和广播,在跨这些平台时很不方便。甚至WinSock在不同版本的Windows上具有不兼容的时间相关的(timing-relatedbug,这些bug在进行非阻塞连接时偶尔会导致失败。

l       陡峭的学习曲线。因为过于细节,掌握操作系统API需要花费很大力气。例如,学会怎样正确的使用POSIX异步I/O[POSIX95]是很困难的。学会使用异步I/O机制编写可移植的(portable)应用程序更加困难,因为它们在不同的操作系统平台上有很大区别。

l       不能扩充以处理不断增加的复杂性。操作系统API定义了如进程和线程管理,进程间通信,文件系统和内存管理等机制的基本接口。然而,当应用程序在尺寸和复杂性方面增长时,这些基本接口不能扩充。例如,一个典型的UNIX进程只允许小于7个连接等待[Ste98]。这个数字对于有大量访问的电子商务服务器来说完全不够,这些服务器必须处理成百或数千个客户端同时请求。

因此,基础网络或系统程序的关键设计难题主要在与在不降低性能的情况下使上边提到的随机复杂性最小化。

对于高级分布式对象计算应用程序来说,服务访问常常涉及调用那些定义了通用服务的可复用组件上的远程操作。这些服务如:命名(naming[OMG97a],商务(trading[OMG98b],以及事件通知[OMG99c]。很多组件模型如EJB[MaHa99]COM+[Box97],以及CORBA组件模型,允许组件为不同的客户端提供不同的服务,这依赖于一些因素,如客户端请求的版本或者客户端的授权级别。因此在这个级别上的一个关键的设计难题在于保证客户端不会访问到无效的或者未授权的组件服务。

解决这个难题是重要的:网络应用程序比单机应用程序在安全防御方面更脆弱,因为它有更多的访问点可以被攻击[YB99]。例如,很多媒体共享网络,如以太网,令牌环,以及FDDI,针对窃听(cable tapping)和抓包(packet snooping)工具提供了有限的内建的(built-in)保护。类似的,网络应用程序必须提防一个主机伪装成其它来访问未授权的信息。尽管一些网络软件库如OpenSSL[OSSL00],支持验证,授权和数据加密,但是用单个API来访问这些安全服务的方案仍然没有被广泛采纳。

另一个关键难题是使服务和应用程序支持静态和动态扩展。扩展以两种方式呈现:

l       组件服务的接口和组件服务间的连通性可以改变,常常在运行期,并且新的服务可以被实现并安装到现有的组件上。

l       分布式系统的性能能够通过重新配置控制多主机处理能力的服务得到提高。

理想情况下,这些组件的配置和重新配置对访问各种服务的客户应用程序应该是透明的。因此另外一个设计难题是:当仅因为某个组件中一个特别的服务被重新配置或者它的加载被重新分布时,要保证整个系统不需要关机,重新编译,重新链接,以及重启。

有些服务是根据需要(on-demand)配置到一个系统中的,并且它的实现在系统最初被设计的时候是未知的,决定怎样访问这样的服务更具有难题性。

很多现代操作系统和运行时环境提供了显式动态链接API[WHO91],这使得应用程序可以根据需要进行配置:

l       UNIX定义了dlopen()dlsym(),以及dlclose()API,它们可以用来显式加载指定的动态链接库(DLL)到一个应用程序进程中,从DLL中提取制定的工厂函数,以及unlink/unload动态库[Ste98]

l       Win32提供了LoadLibrary()GetProcAddr(),以及CloseHandle()API来完成和UNIX DLL API同样的功能。

l       JavaJava.applet.Applet类定义了init()start()stop(),以及destroy()钩子方法来支持动态加载的applet的初始化,开始,停止,以及终止。

然而,根据需要将服务配置到应用程序中需要比动态链接更多的机制,它需要(重新)配置策略的模式。这里的设计难题有两个方面,首先,应用程序必须能够导出新的服务,尽管它可能不知道服务的详细接口。其次,应用程序必须透明且健壮的将这些服务整合进它自己的控制流程和处理序列,甚至在运行期。

第二章,“服务访问和配置模式” 介绍了四个模式来设计高效的编程API,这些API用来在单机的和网络的软件系统以及应用程序中访问并配置服务及组件。这些模式是包装外观(WrapperFaçade47),组建配置器(ComponentConfigurator75),解释器(Interceptor109),和扩展接口(ExtensionInterface141)。

难题2:事件处理

由于系统变得日益网络化,支持事件驱动应用程序的软件开发技术日益普遍。事件驱动的应用程序区别于传统的“self-directed”控制流程[PLoPD1]的三个特性:

l       应用程序的行为由异步产生的外部或内部事件触发。常见的事件源包括设备驱动,I/O端口,传感器,键盘或鼠标,信号,定时器,或其它异步软件组件。

l       大部分事件必须被迅速的处理以防止CPU空闲,提高响应时间,以及防止实时硬件设备失败或损坏数据。

l       有限状态机[SGWSM94]可以用来控制事件处理和检测非法转换,因为事件驱动应用程序通常很少或不控制事件到达的顺序。

因此,事件驱动应用程序常常通过所谓的“控制反转(inversion of control)”[John97]来组织成分层架构[POSA1]

 

3

l       最底层是事件源,该层检测并从各种硬件设备或位于操作系统底层的软件设备驱动接收事件。

l       接下来一层是事件多路分离器,如select()[Ste98],该系统调用等待各种事件源的事件到达,然后将事件分派给相应的事件处理器函数。

l       事件处理器,与应用程序代码一起,构成了另外一层,该层执行应用程序特定的处理来响应回调,这就是“控制反转”这个术语。

在这个事件驱动架构中分离了关注点,使得开发者将精力几种在应用层的功能上,而不是为每个新的系统和应用程序重复编写事件源和多路分离器层。

在很多网络系统中,应用程序通过点对点(peer-to-peer)协议进行通信,如TCP/IP[Ste93],并且使用上边提到的分层事件驱动架构来实现。在这个架构中,节点间交换的事件分别扮演四种不同的角色[BI91]

 

4

l       PEER1,即client initiator程序,调用一个send操作传送request事件到PEER2,即service provider程序。事件中包含PEER1PEER2合作必须的数据。例如,一个PEER1请求会包含CONNECT事件来初始化一个双向连接,或者一个DATA事件来传送一个需要在PEER2上远程执行的操作和它的参数。

l       PEER2 service provider程序通过收到indication事件被通知有request事件到达。然后PEER2调用receive操作得到并使用indication事件数据来执行操作。PEER2的分离器层常常会等待来自很多peerindication事件的集合。

l       PEER2 service provider程序处理完indication事件后,它调用一个send操作传送response事件到PEER1,回应最初的事件并返回某种结果。例如,PEER2可以回应CONNECT事件来作为开始“握手”的一部分,或者可以在一个可靠的双向远程方法调用中回应DATA事件。

l       PEER1 client initiator程序通过收到一个completion事件被通知有response事件到达。此时它可以用receive操作获得最初它发给PEER2 serviceproviderrequest事件的结果。

如果发送完request事件之后,PEER1程序阻塞等待接收包含了PEER2responsecompletion事件,它就被成为同步(synchronous)客户端。相反,如果PEER1发送了request之后不阻塞,他就被成为异步(asynchronous)客户端。异步客户端可以通过异步机制接收completion事件,例如UNIX信号处理者[Ste99]或者Win32 I/Ocompletion ports[Sol98]

传统网络应用程序通过底层操作系统API来检测,分离,以及分派各种类型的控制和数据事件,如Socket[Ste98]select()[Ste98]poll()[Rago93]WaitForMultipleObjects(),一节I/O completionports[Sol98]。然而,使用这些底层API增加了事件驱动编程的随机复杂性。使用这些底层API进行编程也增加了代码重复,而且因为会将一个应用程序的I/O和多路分离操作与它的连接和并发机制耦合在一起,会增加维护工作。

第三章,“事件处理器模式”,介绍四个模式来描述怎样在网络软件框架中高效的初始化,接收,多路分离,分派,以及处理各种类型的事件。这些模式是反应器(Reactor179),前摄器(Proactor215),异步完成标记(AsynchronousCompletion Token261),以及接收器-连接器(Acceptor-Connector285)。

难题3:并发

并发是一个术语,该术语指使用一个或者更多进程或线程来同时执行服务处理任务的一组策略和机制。很多网络应用程序,特别是服务,必须并发的处理来自很多客户端的请求。因此,并发网络软件的开发者常常需要精通各种进程和线程管理机制。

进程是一组资源的集合,如虚拟内存,I/O句柄,以及信号处理器,进程为程序指令的执行提供了上下文(context)。在上一代操作系统中,如BSDUNIX[MBKQ96],进程只有一个控制线程。

线程是在进程上下文[Lew95]中运行的指令序列。除了一个指令指针外,线程由一些资源组成,如一个记录函数活动的运行时堆栈,一组寄存器,以及线程专用(thread-specific)的数据。

单线程进程的使用简化了某种并发程序,如远程登录,因为不同的进程在没有程序员明确干预的情况下没法彼此干涉。然而,使用单线程进程很难开发网络应用程序。例如,单线程BSDUNIX服务在处理一个客户请求的时候不能因为要阻塞一段时间而导致降低对其它客户端的响应。尽管通过使用像信号驱动SocketI/O这样的技术或fork多个进程可以解决这些限制,但最终的程序会变得复杂而低效。

 

现代操作系统克服了单线程进程的限制,提供了多线程并行机制,支持创建多个进程,每个进程可以包含多个并行的线程。在这些操作系统中,进程作为资源分配和保护的单位,在被硬件保护的地址空间中运行。类似的,线程作为可执行单位运行在进程地址空间中,该空间与其它线程共享。

5

流行的线程编程模型,如POSIXPthread[IEEE96]Win32线程[Sol98],提供四点好处:

l       它们通过使用硬件的和软件的并行处理能力透明的提高了性能。

l       它们通过允许程序员叠加计算操作和通信服务处理来明确的提高性能。

l       它们通过在应用程序中给不同的线程关联不同的服务处理任务来提高交互程序的可察觉响应时间,这些程序如图形用户界面。

l       它们通过允许多个服务处理任务使用同步编程抽象(译注:如互斥体等)独立的运行来简化应用程序设计。例如双向方法调用(two-waymethod invocation)。

然而,开发高效的,可预测的,可扩展的,并且健壮的并发应用程序[Lea99a]非常困难。其中一个复杂性的来源是多线程所共有的危险,如竞争条件和死锁[Lea99a]。另外一个复杂性的来源是现有开发方法,工具和操作系统平台的限制。特别是,现在的硬件和软件平台很多,这使得开发能够运行在多个操作系统上的并发应用程序和工具变得复杂。

例如,优雅可移植的停止多线程程序是困难的。问题来源于不同操作系统上不一致的线程取消语义(threadcancellation semantics[Lew95],如POSIX/UNIXWin32,以及像VxWorksLynxOS这样的嵌入式系统。类似的,在不同的操作系统支持高级线程特性区别很大,这些特性如线程专用存储(thread-specificstorage475),detachedthread[Lew95],实时调度[Kan92],以及scheduleractivations[ABLL92]。因此,直接使用操作系统API编程实现可移植的并发应用程序是不可行的。

通用目的的设计模式,如适配器(Adapter[GoF95]和包装外观(Wrapper Façade47),可以用来避免并发软件受上边谈到的API的随机复杂性的影响。另外,一些现成的“基础设施”中间件,如ACE[Sch97]JVM,现在被广泛应用并且用这些模式实现了高效的可复用的操作系统封装层。然而,即使采用了这些中间件,因为并发应用程序开发固有的复杂性,很多难题依然存在,固有复杂性包括:

l       确定一个高效的应用程序并发架构,能够最小化并发应用程序[SchSu95][SKT96]中的上下文切换,同步,以及数据复制/移动的成本。

l       设计复杂的包含同步和异步服务处理任务的并发系统来简化编程而不降低执行执行效率[Sch96]

l       选择合适的原生(primitive)(译注:操作系统本身的)同步机制来提高多处理器[McK95]上的并发应用程序的性能,避免竞争条件,以及减少维护成本。

l       消除并发或实时应用程序中不必要的线程和锁,以此来提高性能或简化资源管理,从而不会有失正确性,导致死锁,或者不正当的阻塞应用程序运行。

解决这些固有复杂性需要的不止通用目的设计模式和可移植基础设施中间件线程API。相反,这需要开发者学会并融汇开发并发应用程序,组件,框架和系统架构的成功模式。

第五章,并发模式,包含5种模式,这五种模式定义了组件,子系统以及整个应用程序的各种类型的并发架构。它们是活动对象(ActiveObject369),监视器对象(Monitor Object399),半同步/半异步(Half-Sync/Half-Async423),领导者/跟随着(Leader/Followers447),以及线程专用存储(Thread-SpecificStorage475)。

难题 4:同步

很多网络程序的效率,响应度,以及设计可以通过使用上边提到的并发机制和模式而受益。例如,应用程序中的对象可以并发的运行在不同的线程中来简化程序结构。如果有多处理器,可以通过真正的硬件并行来实现线程以提高性能。

除了难题3,并发中提到的复杂性,由于需要同步的访问共享资源,并发编程比顺序编程更难。例如,并发运行的线程可以同时访问对象和全局变量,就有破坏他们内部状态的隐患。为了防止这个问题,不应该同时执行的对象或函数的代码可以放到临界区(critical section)中进行同步。临界区是指一系列遵循以下不变式的指令:当一个线程或进程在某个,临界区中执行时,任何其它线程或进程不能在该临界区中执行[Tan95]

 

6


在面向对象程序中常见的实现临界区的方法是硬编码某种类型的锁对象到类或组件中。例如,一个互斥选择(mutualexclusion)(互斥体,Mutex)对象是一种必须被顺序请求(acquired)和释放(released)的锁。如果多个线程尝试同时请求互斥体,只有一个线程能够成功。其它线程必须等待直到互斥体被释放,释放之后,所有等待的线程会重新竞争锁[Tan92]。其它类型的锁,如信号量(semaphore)和读/写锁(readers/writerlock),使用类似的请求/释放协议[McK95]

不幸的是,通过底层操作系统API来编程使用这些锁机制导致两个缺点:

l       容易出错。进入临界区前显式请求一个锁并且在退出临界区后显式释放它竟是出人意料的困难。特别的,如果一个临界区有多个返回路径,锁必须在所有路径被显示释放。这种用法是讨厌的编程错误的常见来源,因为很容易在某个返回路径忘记释放,特别是如果在该部分有异常抛出时。如果锁没有被释放,当接下来有线程进入该临界区使就会发生死锁,并且会无止境的阻塞。

l       不灵活并且低效。依赖于程序运行的上下文,性能要求会迫使用不同的锁类型来实现临界区。例如,如果原来使用互斥体的程序要运行在大规模多处理器平台上,通过将锁机制改成读写锁会提升性能。这种类型的锁允许多个读线程并行的访问共享资源[McK95]。如果锁的原生(primitives)机制被硬编码到软件中的每个用到的地方,改变锁机制变得不必要的麻烦并耗时。

第四章,同步模式,描述了4中模式,缓和了上边描述的问题,简化了并发系统中的串行化和加锁。这些模式是区域锁(ScopedLocking325),策略化锁(StrategizedLocking333),线程安全接口(Thread-SafeInterface345),以及双检锁优化(Double-CheckedLocking Optimization353)。

其它网络软件难题

上边的四个话题覆盖了服务访问和配置,事件处理,并发和同步,描述了本书中的模式解决的网络软件开发的核心难题。然而,网络应用软件开发者必须解决其它话题领域的问题,如可靠性(dependability),服务命名(service naming),位置选择(locationselection)。尽管这些话题超出了本书的范围,我们依然在下边讨论这些重要的难题来阐明这个领域的范围。

可靠性。采用网络架构的一个原因是增加可靠性并防止单点失败。具有讽刺意味的是,网络程序常常需要更多的努力来获得单机程序提供的同样级别的可靠性。在单机程序中检测服务失败相对简单,因为操作系统拥有系统服务和外部设备的健康和状态的所有信息。因此,如果当一个资源失效时,操作系统可以很快的通知应用程序。类似的,如果有个服务或者设备失败,操作系统可以终止程序,并留下确定的退出状态。

相反,由于不完整的系统状态信息,在网络程序中检测错误更加困难。例如,网络程序设计成允许一定数量的等待抖动(latencyjitter)和非确定(non-determinism)。因此,一个客户端可能不会检测到服务器异常终止,直到有价值的信息丢失。类似的,服务器的应答会在网络中丢失,导致客户端重复发送请求。

有几种技术可以增加程序可靠性:

l       再激活(Reactivation)。程序和服务可以运行在监视进程(monitor daemon)的控制下,监视进程检测它们的状态并在它们意外终止时重启服务器。服务器定期通过心跳(heart-beat)消息向与它们关联的监视进程报告自己的当前状态。如果在一个定义好的时间间隔内某个消息没有到达,监控进程认为该服务器已经异常终止并重启它。

l       复制(Replication)。程序和服务在复制管理器(replica manager)的控制下通过网络运行在多个位置[GS97]。复制管理器可以通过“activereplication”不断的更新服务副本,或者只在主服务失败的时候通过“passivereplication”更新。复制框架[FGS98]提供了各种监视(monitoring),成员(membership),统一(consensus),以及消息(messaging)机制来帮助加强程序的可靠性。

大量的研究资料和工具将精力集中在增加进程的可靠性[GS97][BR94][HK93]或分布式对象上[CRSS+98][FGS98]。一些工作已经用模式的方式进行了记录[IM96][Maf96][SPM98][ACGH+96][Sta100],然而很多研究还没有。随着FaultTolerant CORBA规格以及实现了该规格的ORBs的采用,更多的程序开发者能够使用fault-tolerant分布式对象计算模式来记录他们的经验。

服务命名和位置选择。单机程序通常通过对象和函数内存地址来标识他们的各个服务。相反,网路程序需要更精致的机制来命名,定位,以及选择它们的远程服务。IP主机地址和TCP端口号是CORBADCOMjava RMIDCE,和SunRPC中通用的远程服务寻址方法。然而这些底层机制在大规模网络系统中常常不够用,因为它们很难方便的管理,并且方式不够明确。例如,TCP端口5000在被不同的厂商或网络管理员配置的主机上不一定表示同样的服务。

因此分布式对象计算和RPC中间件提供了location broker来允许客户端通过高层的名字而不是底层的IP地址和TCP端口号来访问服务。Location broker简化了网络系统管理,并且通过自动完成以下任务,促进了整个网络上的服务更灵活,更动态的部署:

l       名字绑定(Name binding)。这个任务将服务名称绑定在它们的当前主机/进程位置上。例如,SunPRCrpcbind工具在一个远端系统上执行端口映射任务[Sun88]。更多通用的名字绑定机制,如DCECell directory ServiceCDS[RKF92]LDAP[HSGH99]X.500[SS99],以及CORBA的命名服务(Naming Service[OMG97a],也可以使用。这些服务在一个管理区域(例如:一个局域网或者一个内部网)内实现了一个全局名字空间。

l       服务定位(Service location)。一个服务或资源常常可以通过复制,运行在网络上的几个位置上,来提高可靠性。在这个例子中,程序可以通过一个locationbraker来确定那个服务提供者是最合适的。例如,CORBATrading Service允许客户端通过与服务关联的一系列属性来选择远程对象,例如通过确定大厦里的哪个打印机有Postscript支持,彩色打印,1200dpi的分辨率,以及足够的纸张来选择打印机。

与名字绑定和服务定位相关的模式已经在POSA1[Doble96][JK00]中出现过。

在本章剩下的部分,我们会在我们的讨论中参考并使用UNIXWin32操作系统固有的API。你可能要保留一份参考手册如[Ste99][Ste98][Lew95][Ric97]在手边,如果一些话题或术语不熟悉的时候进行澄清。

当阅读本书中的模式时,认识到术语“client”和“service”不是指软件或硬件组件不变的属性很重要。相反的,他们是在特定的请求/应答交互中的角色[RG98]。例如,在一个对称的点对点系统中,PEER1PEER2在它们交互的不同时期里既可以是client initiator也可以是service provider

 

 

原创粉丝点击