
2.4 分布式系统的结构
2.4.1 分布式系统的硬件
尽管所有的分布系统都包含多个CPU,但硬件的组织仍然存在多种不同方式,特别是硬件相互连接以及通信的方式更是多种多样,在本节中,对分布式系统的硬件进行简要的讨论,着重讨论计算机间相互连接的方式。下一节将讨论与分布式系统的软件相关的部分问题。
近年来,提出了多种针对多CPU计算机系统的分类方案,但是没有一种能够真正流行起来并得到广泛采用。这里,只关心那些由独立计算机的集合构建起来的系统。在图2-2中,把所有的计算机分为两类:共享存储器的计算机系统和不共享存储器的计算机系统。前者一般称为多处理器系统(Multiprocessor),而后者有时称为多计算机系统(Multicomputer)。它们之间的本质区别是:在多处理器系统中,所有CPU共享一个物理地址空间。例如,如果其中某个CPU向地址1000写入值44,那么随后任何其他CPU在读取自己的地址1000时都会得到44这个值。也就是说,存储器是由所有机器共享的。
相比之下,在多计算机系统中,每台机器都拥有自己的专用存储器。如果某个CPU向其地址1000写入44这个值,随后其他CPU在读取地址1000时会得到该地址单元内原来存储的值,因为那个将44写入自己存储器的CPU并不能影响到其他CPU的存储器。多计算机系统的一个常见例子是通过网络连接起来的一组个人计算机。
在这两个类别中,还可以根据网络互联体系结构进行进一步划分。在图2-2中,将这两类分别称为总线型(Bus)和交换型(Switched)。总线型指存在一个连接所有机器的网络、底板、总线、电缆或其他介质。有线电视采用的就是总线型结构,即有线电视公司在街道下面敷设一条线路,所有的用户使用分接头从该线路把信号引到自己的电视机。
交换型系统不像有线电视那样具有一条单独的主干线,而是各机器之间都用独立的线路相连。在实际使用中可以有多种不同的布线方式。消息沿着这些线路传输,在传输的每一步都需要做出专门的交换决策,以使路由消息沿着特定的线路发送。国际公用电话系统采用的就是这种方式。

图2-2 分布式计算机中的处理器和存储器的不同组织方式
可以把分布式计算机系统进一步划分为两类:一类是同构的(Homogeneous),另一类是异构的(Heterogeneous)。这种划分只对多计算机系统有意义。在同构式多计算机系统中,本质上,系统只有一个单独的互联网络,该网络在系统中全部使用同一种技术。同样,所有处理器也都是相同的,而且一般能够访问相同的专用存储器。同构式多计算机系统更多地作为并行系统(计算单个问题)使用,就像多处理器系统一样。
相比之下,异构式多计算机系统中可能包含许多不同的独立计算机,它们通过不同的网络相连。例如,分布式系统可能由一组不同的局域计算机网络构成,这些局域的计算机网络通过FDDI或ATM交换主干系统相连。
2.4.2 分布式系统的结构模型
在上述三类分布式系统硬件中,异构式多计算机系统是分布式多媒体计算机系统中最重要的、也是必然要演化成的一种基本的结构。在异构式多计算机系统中,各个独立的计算机之间常按下述结构模型进行组织。
1.工作站/服务器模型
在工作站/服务器模型中,工作站通常是性能相对低的计算机硬件,如PC、移动终端等,在工作站上运行的是资源需求较少的应用程序,如Web浏览器、媒体播放器、人机交互界面等;服务器则一般是性能较高的专用计算机系统,如文件服务器、数据库服务器、流媒体服务器、超级计算机等,为分布式系统中的用户提供数据资源和计算能力等的服务。工作站/服务器模型如图2-3所示。

图2-3 工作站/服务器模型
需要指出的是,工作站/服务器模型是一种分布式系统的硬件组织模型,它与客户机/服务器(Client/Server)进程组织模型讨论的不是一个问题。
2.多处理机池模型
在多处理机池模型中,程序在处理机池中执行,它的优点是资源利用率高,但是终端交互性较差。多处理机池模型如图2-4所示。用户采用终端而不是工作站,灵活性强。PAD为分组装拆器,兼容性好。终端通过集中器接入网中,异构性好。

图2-4 多处理机池模型
3.混合模型
工作站/服务器模型不能执行的大任务可利用处理机池完成,于是产生混合模型,如图2-5所示。

图2-5 混合模型
2.4.3 分布式系统的软件
分布式系统的硬件是重要的,但在更大程度上决定分布式系统的外在特征的是软件。分布式系统很像系统的操作系统。首先,它们在行为上就像底层硬件的资源管理器,允许在多个用户和应用程序间共享资源。这些资源包括CPU、存储器、外围设备、网络以及各种数据。其次,也许是更重要的一点,分布式系统试图通过向应用程序提供一个虚拟机让应用程序在上面运行,以隐藏底层硬件的不但错综复杂而且异构的本质。
为理解分布式系统的本质,要先考察分布式计算机使用的操作系统。供分布式系统使用的操作系统可以粗略地划分为两类:紧耦合系统与松耦合系统。在紧耦合系统中,操作系统主要是以一种简单的全局视图来管理其资源。松耦合系统可以看成是一组计算机,每台计算机都各自运行自己的操作系统,这些操作系统进行协作,使分属于各操作系统的资源和服务得到共享。
紧耦合系统与松耦合系统之间的区别与在上一节中介绍的硬件有关。紧耦合系统一般又称为分布式操作系统(Distributed Operating System,DOS),用于管理多处理器系统和同构式多计算机系统。与传统的单处理器操作系统类似,分布式操作系统的主要目标是隐藏底层硬件管理的复杂性,例如硬件可以由多个进程共享这样的特性。
松耦合网络操作系统(Network Operating System,NOS)用于管理异构式多计算机系统。虽然底层硬件管理也是NOS的重要任务之一,但是它与传统操作系统的主要区别在于它允许远程客户使用本地服务。
必须对网络操作系统的服务进行改进,对分布透明性提供更好的支持,以使其真正符合分布式系统的要求。这些改进导致了中间件的诞生。中间件位于现代分布式系统的核心。
1.单处理器操作系统
传统上,操作系统用来管理只有单个CPU的计算机。这种系统的主要目标是,让用户和应用程序能够方便地共享资源。这些资源包括CPU、主存储器、磁盘和外围设备。资源共享意味着不同的应用程序可以用一种彼此隔离的方式访问同一个硬件,对于其中某个应用程序来说,就好像这个资源是属于它自己的一样。在同一个系统上,几个应用程序可以同时运行,每个程序使用属于自己的一组资源。在这个意义上,操作系统实现了虚拟机(Virtual Machine),向应用程序提供多任务的功能。
在这种虚拟机内共享资源的一个重要特征就是应用程序彼此隔开。例如,不允许出现这种情况:两个独立的应用程序A和B同时执行,应用程序A只简单地通过访问应用程序B的数据,当前所在的主存储器就可以修改属于B的数据。同样,需要确保应用程序只能以操作系统允许的方式来使用提供的功能。例如,通常应该阻止应用程序直接向网络接口复制消息的行为,操作系统应该提供通信原语,只有使用这些原语才能够在位于不同机器的应用程序之间传递消息。
因此,操作系统必须对硬件资源的使用方式及共享方式进行完全控制。大多数CPU都至少支持两种操作模式。在内核模式(Kernel Mode)下,允许所有指令的执行,在执行过程中可以访问所有存储器和寄存器。在用户模式(User Mode)下,对存储器和寄存器的访问受到限制。例如,会禁止应用程序访问超出某个地址范围(由操作系统设定)的存储器区域,并且禁止对设备寄存器的直接访问。在执行操作系统代码的过程中,CPU切换到内核模式。然而,从用户模式切换到内核模式的惟一途径是通过由操作系统实现的系统调用。由于系统调用是操作系统提供的惟一的基本服务,并且由于有硬件协助对存储器和寄存器的访问加以限制,操作系统可以完全控制硬件资源。
尽管存在两种操作模式,但实际上,操作系统的组织结构常常使所有的操作系统代码都以内核模式运行,从而导致操作系统变为一个庞大且单一的程序,运行于单独的一个地址空间。这种方案的缺陷是,常常难以对操作系统进行修改。换句话说,很难在不彻底关闭系统的情况下替换或修改操作系统组件,有时甚至还需要完全重新编译且重新安装。从开放性、可靠性和可维护性的角度来说,采用单一的操作系统并不是一个好办法。
一个更加灵活的方案是将操作系统分成两个部分。第一个部分包括一组硬件管理模块,它们也能在用户模式下执行。例如,存储器管理主要包括跟踪已分配给进程的存储器和空闲存储器。惟一例外是,在设置MMU(存储管理部件)寄存器时,必须以内核模式执行。
第二个部分包含一个小型的微内核(Microkernel),它具有必须以内核模式执行的代码。实际上,微内核只需要包含那些用来设置寄存器、在进程间切换CPU、操作MMU以及捕获硬件中断的代码。另外,微内核还要包含用来将系统调用传递给相应的用户级操作系统模块的代码,微内核对这些用户级的模块进行调用,然后返回它们得到的结果。这种方案的结构如图2-6所示。

图2-6 通过微内核分隔应用程序与操作系统代码
使用微内核有诸多优点。其中一个重要的优点是灵活性:由于操作系统中有一大部分代码以用户模式执行,要在不重新编译或者重新安装整个系统的情况下替换某个模块相对来说更加容易。另一个重要的优点是,原则上,用户级模块可以放置在不同的机器上,例如可以很方便地把文件管理模块放置在一台机器上,而把目录服务放在另一台机器上。换句话说,采用基于微内核的方案可以方便地把单处理器操作系统扩展到分布式计算机上。
微内核有两个严重的缺点。首先,微内核系统的工作方式与当前通用的操作系统不同,而尝试改变任何现状的努力总会遭遇重重阻力。其次,微内核需要进行额外的通信,这样就会损失少许的性能。然而,现在的CPU速度都很快,即使有20%的性能损失也并不严重。
2.多处理器操作系统
对于单处理器操作系统的一个重要的但并非很明显的扩展是,提供对访问共享存储器的多处理器的支持。从概念上,这种扩充是简单的,因为操作系统管理硬件(包括多个CPU)所需的所有数据结构都放在共享存储器中。主要的区别是,这些数据现在可由多个处理器访问,这样就必须避免并发访问的发生,以确保数据的一致性。
然而,许多操作系统,特别是那些供PC和工作站使用的操作系统,无法方便地支持多个CPU主要的原因是,这些操作系统被设计成单一的程序,只能作为单个控制线程执行。对这样的操作系统进行改造以供多处理器系统使用意味着重新设计并实现整个内核。现代操作系统从开始设计时就把支持多CPU作为设计要求之一。
多处理器操作系统的目标是,通过多个CPU支持高性能。一个关键的目标是,使CPU的数目对应用程序透明。要达到这样的透明性,相对比较容易,因为在不同的应用程序(或应用程序的各部分)之间通信时使用的原语与在多任务单处理器操作系统中使用的原语相同。所有的通信是通过操纵位于共享存储器地址中的数据来进行的。只需要保护数据不在同一时刻受到多个访问。这种保护措施是通过同步原语实现的。两个重要的(并且等价的)原语是信号量(Semaphore)和监控器(Monitor)。
可以把信号量想象成一个整型数和两种操作:向下操作和向上操作。向下操作检查信号量的值是否大于0。如果是,它就将值减1,然后继续。如果值为0,就将调用该操作的进程阻塞。向上操作则正好相反,它首先检查当前是否有在调用向下操作时被阻塞的进程。如果有,它将会释放被阻塞的进程之一,然后继续;否则,它将信号量的值加1。被释放的进程在从向下操作中返回后可以继续执行。信号量操作的一个重要特性是,这种操作是原子性的(Atomic),即一旦开始向下或向上操作以后,在操作完成之前(或在进程阻塞之前),任何别的进程都不可以访问该信号量。
众所周知,用信号量编程来进行进程间同步很容易出错,但用于简单的保护共享数据除外。主要的问题是,使用信号量很容易造成代码结构混乱,这与大量使用声名狼藉的goto语句所造成的情况一样。许多支持并发程序设计的现代操作系统提供了另外一种解决方案,它们提供用于实现监控器的库。
形式上,监控器是一种编程语言结构,与面向对象程序设计中的对象类似。可以把监控器想象成一个包含变量和过程的模块,变量只能通过调用监控器中的某个过程来访问。在这种意义上,监控器与对象有相似之处:对象拥有自己的私有数据,只能通过由该对象实现的方法来访问。监控器与对象的不同之处是,监控器在同一时刻只允许单个进程执行监控器中的一个过程。换句话说,如果进程A正在执行监控器中的某个过程(称为“A已经进入监控器”),而此时进程B也调用监控器中的某个过程,那么B将会被阻塞,直到A从调用返回为止(相应地称为“A离开监控器”)。
3.多计算机操作系统
供多计算机系统使用的操作系统与多处理器操作系统在结构上截然不同,它比多处理器操作系统具有更高的复杂性。这种差异是由于系统范围内的资源管理所需的数据结构无法放置在物理上共享的存储器中,从而无法方便地共享这些数据结构而造成的,其惟一的通信方法是消息传递。因此,多计算机操作系统的常见组织结构如图2-7所示。

图2-7 多计算机操作系统的常见组织结构
每个节点都拥有自己的内核,内核中有负负管理本地资源(如存储器、本地CPU、本地磁盘等)的模块。同时,每个节点还包含一个单独的模块,负责对处理器间的通信进行处理,也就是向其他节点发送消息,并从其他节点接收消息。
在每个本地内核上面是一个公共软件层,它实现了操作系统作为支持各种任务并行执行和并发执行的虚拟机的功能。实际上,该层甚至可以提供多处理器计算机的抽象。下面将对此进行简短的讨论。换句话说,它提供了共享存储器的软件实现。该层中还实现了另外一些功能,例如,向处理器指派任务、屏蔽硬件故障、提供透明存储和常规进程间通信。也可以说,该层具备了一般操作系统应该具有的所有功能。
不提供共享存储器的多计算机操作系统只能向应用程序提供消息传递功能。不幸的是,消息传递原语的语义在不同系统间可能有着巨大的差异。要说明它们之间的差异,最简单的方法就是考察是否对消息进行了缓冲。另外,需要考虑发送或接收消息的进程在何时被阻塞。图2-8中显示出在消息传递中可能发生缓冲和阻塞的位置。

图2-8 在消息传递中可能发生阻塞和缓冲的位置
只可能在两个位置(发送端或接收端)对消息进行缓冲。因此,就产生了4个可能的同步点(同步点就是发送进程或接收进程可能发生阻塞的点)。如果在发送端进行缓冲,那么只有在缓冲区满时,阻塞发送进程才有意义,这就是图2-8中的同步点S1。另一种方案是,将消息放入缓冲区的操作会返回一个标志操作是否成功的状态值,这就避免了在缓冲区满的情况下对发送进程进行阻塞。此外,当不存在发送缓冲时,可以在下列三种情况中的一种情况下阻塞发送进程。
(1)消息已发送(同步点S2)。
(2)消息已到达接收者(同步点S3)。
(3)消息已经交付给接收者(同步点S4)。
注意:如果阻塞发生在S2、S3或S4这三个同步点之一,那么发送者的缓冲就没有任何意义。
对接收进程的阻塞只在同步点S3有意义,这种情况只在不存在接收者缓冲或接收者缓冲为空时才会发生。也可以让接收者不断查询是否有传入的消息,但这会导致CPU资源的浪费,或者对传入消息响应过慢,这种过慢的响应可能会使缓冲区溢出,从而不得不丢弃输入的消息。
还有一个问题对于理解消息传递语义很重要,这就是通信是否可靠。可靠通信的关键特征是,在发送者发送消息后,可以从接收者获得消息已收到的确认反馈。在图2-28中,这意味着确保所有消息都能够到达同步点S3。对于不可靠通信来说,没有这样的确认反馈。如果在发送端存在缓冲区,通信可能是可靠的,也可能是不可靠的。同样,如果发送进程在S2点被阻塞,操作系统就不必保证通信的可靠性。
然而,如果操作系统在消息到达同步点S3或S4后才对发送进程进行阻塞,就必须确保通信可靠性,否则可能会处于这样一个尴尬境地:发送者正在等待接收者的确认或者准备进行交付,其间,它的消息却已在传输过程中丢失。阻塞、缓冲与通信可靠性之间的关系如表2-1所示。
表2-1 阻塞、缓冲与通信可靠性之间的关系

在构建多计算机操作系统方面存在的许多问题,对于任何分布式系统来说都是一样重要。多计算机操作系统与分布式系统的主要区别是,前者一般假定底层硬件是同构的,而且受到完全控制。然而,许多分布式系统却常常构建在现有操作系统的基础上。
4.网络操作系统
与分布式操作系统不同,网络操作系统并不假定底层硬件是同构的,也不认为所有硬件应该作为单个系统集中管理。相反,网络操作系统大都构建在一组单处理器系统的基础上,其中每个系统都拥有自己的操作系统,如图2-9所示。系统中的机器及其操作系统可以不同,但是所有机器都通过计算机网络相互连接。同时,网络操作系统提供的功能还包括允许用户使用特定机器上的服务。

图2-9 网络操作系统的一般结构
网络操作系统提供的一项常见服务是,允许用户使用如下命令远程登录到另一台机器上:
rlogin machine
这个命令执行的结果是,将用户自己的工作站变为一台登录到远程机器上的远程终端。假定用户使用的是图形工作站,用键盘键入的命令将发送到远程机器,随后远程机器的回应将显示在用户屏幕上的一个窗口中。如果要切换到另一台远程机器,必须先打开一个新窗口,然后使用rlogin命令来连接到另一台机器。因此,机器的选择完全是手工完成的。
网络操作系统还提供了远程复制命令,用于把文件从一台机器复制到另一台机器。例如:
rcp machinel:filel machine2:file2
上面这条命令会把文件filel从机器machinel复制到机器machine2,并且将复制到machine2上的文件命名为file2。在这里,文件和移动过程同样是显式的,要求用户完全掌握文件所在位置以及命令执行位置。
网络操作系统无疑比分布式操作系统更原始。这两种操作系统之间的主要区别是,分布式操作系统做了相当大的努力以实现完全的透明性,也就是说,力图给出一种单一系统视图。
由于网络操作系统中缺乏透明性,造成了一些明显的缺陷。例如,这种系统难以使用,要求用户显式地登录到远程机器,或者显式地把文件从一台机器复制到另一台机器。管理上也存在问题,由于网络操作系统中的所有机器是彼此独立的,通常只能对它们进行独立管理。结果是,用户只有在机器X上拥有账号才能登录到机器X上。同时,如果用户想在所有机器上使用同一个密码,对这个密码的改动就会导致要求在所有机器上显式地修改该密码。同样的道理,对所有访问许可的维护通常是逐台机器进行的。一旦访问许可各处一致,就没有简单的方法来对访问许可进行修改,只能逐个进行。这种分散的安全方法有时会导致网络操作系统难以防范恶意攻击。
网络操作系统与分布式系统相比也有一些优势。由于网络操作系统的节点是彼此高度独立的,添加或删除机器非常方便。在某些情况下,要添加一台机器所需要做的工作只是把机器连接到公用网络,然后使网络中其他机器知晓这台新机器的存在。在Internet内添加一台新服务器的过程正是如此。为了让Internet上的机器了解到新服务器的存在,需要给出它的网络地址。更好的办法是,给新服务器取一个象征性的名字,再把这个名字和它所对应的网络地址一并添加到DNS中。
5.中间件
无论是分布式操作系统,还是网络操作系统都不能真正符合分布式系统的定义。分布式操作系统并不是用来管理一组“独立”的计算机的,特别是它不能提供对异构计算机的管理;而网络操作系统也没有提供“单个一致的系统”这样的视图。是否可能开发出集这两种系统的优点于一身的分布式系统呢?最好既能够具有网络操作系统的可扩展性和开放性,又能够具有分布式操作系统的透明性和与之相关的易用性。该问题的解决方案可以通过在网络操作系统中使用的附加软件层来实现,这种附加软件层用来或多或少地隐藏网络操作系统中底层平台集合的异构性,并且提高分布透明性。许多现代的分布式系统都是通过使用这种附加软件层实现的。该附加层称为中间件(Middleware)。下面将阐明中间件的一些特性,并对中间件的组成进行详细探讨。
1)中间件所处的地位
很多分布式应用程序直接使用网络操作系统提供的编程接口。例如,通信常常是通过对套接字的操作进行的,套接字允许位于不同机器上的进程互相传递消息。此外,应用程序常常利用本地文件系统接口。这种方法的问题是,使分布难以做到透明。解决方法是,在应用程序和网络操作系统之间放置一个附加的软件层,该软件层提供比操作系统更高层次的抽象。这种软件层位于应用程序和网络操作系统之间,因此称为中间件,如图2-10所示。

图2-10 以中间件形式组织的分布式系统的常见结构
中间件系统的一个重要目标是,对应用程序隐藏底层平台的异构性,因此许多中间件系统都提供一组完整程度不同的服务集。这些服务必须使用系统提供的接口来访问,除此以外没有其他的访问方法。也就是说,一般禁止跳过中间件层直接调用底层操作系统的服务。下面讨论中间件服务。
以下事实很有趣:中间件并不是作为理论上达到分布式透明性的措施而发明的。在网络操作系统诞生且得到广泛应用之后,许多组织发现他们有许多网络应用程序无法方便地集成到单个系统中去。此时,厂商开始在其系统中构建高层的独立于应用程序的服务。这方面的典型例子包括对分布式事务和高级通信功能的支持。
当然,就中间件究竟应该是什么样的这个问题达成一致并不容易。解决这个问题的一种方法是建立一个组织,由它来制定中间件解决方案的通用标准。现在已经有了很多这样的标准,但它们彼此之间通常互不兼容;更有甚者,来自不同厂商的遵循同一个标准的产品也很少能够协同工作。当然,用不了很久就会有人提供“上层件”来解决这个问题。
2)中间件模型
为了使分布式应用程序的开发和集成尽可能简单,多数中间件是基于某种模型(或称为范型)来对分布及通信进行描述的。其中一种相对简单的模型是将任何东西都作为文件来处理。这是一种在UNIX中率先采用的方法,包括I/O设备(如键盘、鼠标、磁盘、网络接口等)在内的所有资源都作为文件来处理。文件在本地还是在远程从本质上来说并没有区别。应用程序打开文件,读出或写入一定数量的字节,然后将文件关闭。由于文件可以由多个进程共享,所以只需通过访问同一个文件就可以实现通信。
以分布式文件系统为中心的中间件所采用的方式与UNIX的方式相近,但没有那么严格。在许多情况下,从只对传统文件(也就是只用于存储数据的文件)支持分布透明性这个意义上来说,这样的中间件实际上只比网络操作系统前进了一小步。例如,仍然要求进程在特定机器上显式启动。基于分布式文件系统的中间件已得到证明具备一定的扩展能力。正是这个优势使它流行起来。
另一种重要的早期中间件模型是基于RPC(Remote Procedure Call,远程过程调用)的。在这种模型中,重点在于通过允许进行调用位于远程机器上的过程实现来隐藏网络通信。在进行这样的过程调用时,调用参数被透明地传输到远程机器上,随后在远程机器上执行该过程,执行完毕后将结果发送给调用者,因此看起来过程调用像在本地执行的一样,除了也许有一定的性能损失之外,发出调用的进程并不知道有网络通信发生。将在下一章中讨论远程过程调用。
随着面向对象的编程方式越来越流行,很明显,如果过程调用可以超越机器之间的界限,那么以一种透明的方式调用驻留在远程机器上的对象也应该是可以做到的。这种想法导致很多中间件系统提供一种分布式对象(Distributed Object)的概念。本质上,分布式对象是实现了一种特殊接口的对象,这种接口向对象的用户隐藏了对象的所有内部细节。接口包含且只包含由对象实现的方法。进程只能看到对象的接口。
分布式对象的实现方式常常是把每个对象本身放置在一台机器上,并允许在其他机器上使用对象的接口。当某个进程调用一个方法时,进程所在机器上的接口实现只是简单地把方法调用转换成一个消息,再把该消息发送给对象。该对象执行所请求的方法,将执行结果送回给接口实现。接口实现再将收到的应答消息转换成结果,把结果交给进行调用的进程。就像使用RPC一样,进程本身并不知道进行了网络通信。
万维网例子最有力地证明了模型给网络系统使用所带来的简化。Web的成功应该主要归功于分布式文档(Distributed Document)这种极为简单但却高效的模型。在Web的这种模型中,信息被组织成文档,每个文档驻留在世界上某处位置透明的机器上。文档中包含指向其他文档的链接。通过打开某个链接,可以从该链接指向的文档所在的位置将文档取回,并把它显示在用户屏幕上。文档的概念并不限于基于文本的信息。比如,Web也支持音频和视频文档,以及各种基于图形的交互式文档。
3)中间件服务
很多中间件系统通常提供多种通用的服务。不管采取何种方式,各种中间件都有一个共同的目标,即力图实现访问透明性。这个目标是通过提供高层通信功能(Communication Facility)以隐藏通过计算机网络进行的低层消息传递来达到的。这些功能彻底取代了由网络操作系统提供的传输层程序设计接口。支持通信的具体方式随中间件系统向用户和应用程序提供的分布模型的不同而有较大的差异。另外,许多中间件系统,如分布式文件系统和分布式数据库,还提供对远程数据的透明访问功能。像Web那样以透明的方式取回文档是高层(单向)通信的另一个例子。
中间件提供的一种常见服务是命名(Naming)服务。命名服务允许实体被共享和查询(就像在目录中一样),这种服务可以比做电话簿和黄页。虽然命名服务乍看起来好像很简单,但是考虑到可扩展性就不简单了。问题是:在大规模系统中,如果要高效地进行名字查询,就必须假设命名的实体所在地址是固定的。在万维网中就做了这个假设,其中的文档目前是用URL命名的。URL中含有该URL指向的文档所在的服务器名,因此如果文档转移到了另一台服务器上,它原来的URL就失效了。
许多中间件系统提供存储方面的特殊功能,这些功能也称为持久性(Persistence)。如果采用最简单的形式,持久性就可以由分布式文件系统来提供,但是更高级的中间件已经在本身的系统中集成了数据库,或者提供了将应用程序连接到数据库的工具。
在数据存储发挥重要作用的环境中,通常提供一些分布式事务(Distributed Transaction)的功能。事务的重要属性之一是,它允许多个读/写操作以原子方式进行。原子方式意味着事务要么成功(在这种情况下,所有的写操作可以得到实际执行),要么失败(在这种情况下,事务涉及的所有数据都不受影响)。分布式事务用于对分散在多台机器上的数据进行操作。特别是在面临要求实现故障屏蔽这一分布式系统难以做到的功能时,很有必要提供分布式事务这样的服务。不幸的是,对分布在多个本地机器上的事务难以进行扩展,更不用说分布到地域上分散的机器上了。
最后,几乎所有用于非实验性环境的中间件系统都提供安全功能。与网络操作系统相比,中间件存在的安全问题更加普遍。在原理上,中间件层不能依赖底层的本地操作系统来提供对整个网络安全的支持。因此,部分安全功能必须由中间件层本身进行重新实现。由于必须与系统扩展性方面的要求结合起来考虑,安全性已成为分布式系统中最难实现的服务之一。
4)中间件与开放性
现代分布式系统一般构建成适合于多种操作系统的中间件。在这种方式下,为某个特定分布式系统建立的应用程序就与操作系统无关了。不幸的是,这种无关性常常演变为对特定中间件的强依赖性。产生这个问题的原因是,中间件的开放性往往不像开发商宣称的那么强。
真正开放的分布式系统是由完整的接口定义进行说明的。完整意味着实现系统所需的一切内容都确实得到了说明。接口定义的不完整性将会导致系统开发人员被迫添加自己的接口,结果就会发生这种情况:两个遵循同一标准的开发组各自开发了一个中间件系统,然而为其中一个系统编写的应用程序无法方便地移植到另一个系统上。
同样糟糕的还有这样的情况:接口定义的不完整性导致两种不同的实现无法互操作,尽管它们实现了完全相同的一组接口,不同的只是底层协议。例如,如果两种不同的实现都依赖于各自底层网络操作系统提供的互不兼容的通信协议,就不能实现互操作性。必须采用相同的中间件协议和中间件接口。
另一个例子是,为了确保不同实现间的互操作性,必须以相同的方式来引用不同系统中的实体。如果某个系统中的实体通过URL来引用,而其他系统中的实体却通过网络地址来引用,这种交叉引用就会出问题。在这种情况下,接口定义必须准确规定引用实体的形式。
2.4.4 客户机/服务器模型
分布式系统组织结构的核心是系统中的进程组织问题。尽管关于分布式系统的许多问题众说纷纭,但是许多研究人员和业内人士都赞同这样一个观点:以客户机向服务器请求服务这样一种模式来进行思考,将会有助于理解和管理分布式系统的复杂性。在本节中,只对基本的客户机/服务器模型进行初步讨论,对于上述问题,以及针对分布式多媒体计算机系统的进程组织模型,将在第5章做进一步的研究。
在基本的客户机/服务器模型中,可以将分布式系统内的进程划分为两组(它们之间可能有相互重叠的部分)。服务器(Server)是实现某个特定服务的进程。这种服务的例子包括文件系统服务和数据库服务。客户(Client)是向服务器请求服务的进程,它向服务器发送一个请求,随后等待服务器的应答。这种客户机/服务器的交互也称为请求—应答行为,如图2-11所示。

图2-11 客户机与服务器之间的一般交互
如果底层网络非常可靠(如在多数局域网中),客户机与服务器之间的通信可以利用简单的非连接协议来实现。在这种情况下,如果客户机想请求某个服务,只要打包一条消息,在消息中写明它需要的服务以及必要的输入数据,然后将打包的消息发送给服务器。服务器在收到请求之后对其进行处理,然后将处理结果打包成一条应答消息,将该应答消息送回客户机。
使用非连接的协议有一个明显的好处是效率高。只要消息不丢失且没有遭到破坏,上面描述的请求/应答协议就能够工作得很好。不幸的是,使协议能够处理偶然的传输故障不是一件简单的事情。我们所能做的可能只是让客户机在未收到应答消息的情况下重新发送请求。然而问题是,客户机无法探知究竟是原来的请求消息丢失了,还是应答消息的传输出现了问题。如果是应答消息丢失了,那么重新发送请求将导致相同的操作被执行两次。如果这个操作类似于“从我的银行账户转出10000美元”,那么,无疑更好的办法还是报告发生了一个错误,而不是重新发送请求。另外,如果这个操作是“请告知我的账户还有多少钱”,那么重新发送请求当然是可以接受的。不难看出,这个问题没有一定的解决方法。
还可以选择另一种方法:许多客户机/服务器系统采用可靠的面向连接的协议。虽然把这种性能相对较低的协议用在局域网内并不是很合适,但是在通信不可靠的广域网系统中,这种协议工作得非常好。例如,几乎所有Internet应用协议都是基于可靠的TCP/IP连接的。在这种情况下,当客户机请求服务的时候,它首先与服务器之间建立一个连接,然后再发送请求。服务器一般使用同一个连接把应答消息送回给客户机,然后断开该连接。问题是,建立连接和断开连接的代价相对来说较高,特别是在请求消息和应答消息相对较小时更是这样。