常规过程调用的概念性模型
远程过程调用模型主要来自于传统编程语言中的过程调用机制。过程调用提供了一个抽象,它允许程序员将一个程序划分为一个小的、可管理的、易于理解的片段。
过程模型的扩充
远程过程调用模型使用了和常规程序一样的过程抽象,只是它允许一个过程的边界跨越两台计算机。
在程序可以使用远程过程调用之前,必须加入允许程序与远程过程通信的协议软件
常规过程调用的执行的返回
如下图展示了控制流从主程序传到两个过程,然后返回。
根据过程执行模型,单个控制线程或者执行线程流经所有过程。计算机从一个主程序开始执行,并一直继续下去,知道遇到一个过程调用。这个调用使执行转入到某个指定的过程代码并继续执行。如果遇到了另一个调用,计算机就转入到第二个调用。
程序继续在所调用的过程中执行,直到遇到一个返回(return)语句。这个返回语句使得程序恢复到紧挨着最后一个调用之后的那点
在概念模型中,任何时候都只有一个执行线程在继续。因此,在计算机执行对一个过程的调用时,另一个过程就必须停止。计算机挂起调用进程,在调用过程中,将这个过程的所有变量值全部冻结。之后,当执行由过程调用返回时,计算机在调用者中恢复执行,而且所有变量的值又可以使用了。被调用过程可以进一步使用过程调用,因为计算机记住了调用的次序,而且总是返回最近执行的调用者
分布式系统中的过程模型
客户-服务器和RPC之间的类比
远程过程调用概念提供了强有力的类比方法,它允许程序员以一种它所熟悉的环境来思考客户和服务器的交互。
- 远程过程调用把控制权传递给被调用的过程。也像常规的过程调用一样,在调用进行中,系统把调用过程的执行挂起,而只允许被调用过程执行。
- 当远程程序发出响应时,这对应于在常规过程调用中执行return。控制权返回给调用者,被调用过程停止执行。
- 不同的是,常规过程会一直保持在inactive状态,直到得到控制流。
- 但是,远程系统中必须由一个服务器进程,并且在接收到来自某个客户的第一个请求之前,它一直在那里等待计算某个相应。
- 另外,常规过程往往接收很少的参数和返回很少的结构,而服务器可以在TCP连接上接收和返回任意的流
本地和远程调用的行为不是完全一致的。
- 首先,网络延时会使远程过程调用的开销比常规过程调用昂贵。
- 第二,由于被调用的过程与调用过程位于同一个地址空间上,所以常规程序可以把指针作为参数传递。远程调用却不能这样,因为远程过程与调用者运行在完全不同的地址空间中。
- 第三,因为远程过程不能共享调用者的环境,它就不能直接访问调用者IO描述符会在操作系统功能。比如,远程过程就不能在调用者的标准差错文件中直接写入差错消息
把分布式计算看作是单个程序,这个程序穿过网络把控制权传递给一个远程过程,在其完成后将控制权返回。这种方式有助于程序员指明客户-服务器的交互;它使分布式计算的交互同我们所熟悉的过程调用以及返回的表示方法联系起来
远程过程调用定义
Sun公司定义了一个特定形式的远程过程调用,它被称为Sun RPC,或者开发网络计算(Open Network Computing, ONC)RPC或者简称为RPC。ONC远程过程定义已经被广泛接受,被用作很多应用软件的实现机制,其中包括网络文件系统(Network File System, NFS)
- ONC RPC定义了调用者(客户)发出的调用服务器中某个远程过程的报文的格式、参数的格式以及被调用过程返回给调用者结构的格式。
- 它允许调用程序使用UDP和TCP来装载报文,它利用XDR来表示过程的参数以及RPC报文首部中的其他条目。
- 最后,除了协议说明之外,ONC RPC号包括一个编译系统,能帮助程序员自动构建分布式程序。
远程程序和过程
ONC RPC通过定义远程执行环境而扩展了远程过程调用模型。
- 它定义了一个远程程序,把它作为在远程机器上执行的软件的基本单元。
- 每个远程程序对应于我们所设想的服务器,它包括一组(一个或者多个)远程过程以及全局数据。
- 在远程程序中的所有过程都能共享它的全局数据。
因此,一组密切合作的远程过程可以共享状态信息。比如,如果想要实现远程数据块,可以构造远程程序,这个程序包含装载共享信息的数据结构,还有三个维护这些信息的远程过程:insert、delete、loopup。如下图所示,远程程序中的所有过程可以共享同一个数据库
减少参数的数量
因为多数编程语言使用位置记法表示参数,所以含有众多参数的过程调用可能含义阅读。程序员可以把很多参数都收集到数据聚合体中(比如,结构体),并且把结构聚合体作为单个参数来传递,通过这种方法可以解决参数过多的问题。
使用结构来代替多个参数可以使得程序更加可读,因为结构字段的名字可以作为关键字来使用,它告诉读者每个参数如何使用
标识远程程序和过程
- ONC RPC的标准说明,在某台计算机上执行的每个远程程序都必须分配一个唯一的32比特整数,调用者使用该整数来标识这个程序。
- 此外,RPC为一个给定的远程程序中的每个远程过程分配了一个整数标识符。这些过程按序计数:1、2、…、N(按照管理,数目0永远保留作为回显过程(echo procedure),用来测试该远程程序是否能被调用)。从概念上来说,在一台给定远程计算机上的某个指定远程过程可以由一个数对来标识。
(prog, proc)
在此,prog标识远程程序,proc标识程序中的某个远程过程。为确保由单个单独的组织所定义的程序号不会冲突,ONC RPC已将程序号的集合划分为八组,如下图所示:
Sun公司管理第一组标识符,它允许任何人申请一个标准的RPC程序号。因为Sun公布了这种分配,所有使用RPC的计算机都使用标准的值。在第一组中,有229个程序号可供使用。Sun只分配了其中下一部分。比如:
适应远程程序的多个版本
除了程序号之外,ONC RPC还包括针对每个远程程序的整数型的版本号。程序的第一个版本往往分配给版本号1。以后的每一个版本都收到一个唯一的版本号
版本号提供了一种能力,它可以使程序不必获得新的版本号就能改变某个远程过程的细节。在实际中,每个RPC报文通过一个三元组来标识某台给定计算机上所期望的接受者,这个三元组是:
(prog, vers, proc)
在此,prog标识远程程序,vers指明报文所要发往的程序的版本号,proc指明该远程程序中的某个远程过程。RPC的规约允许计算机同时运行远程程序的多个版本,这就可以运行改进程序过程是进行从容迁移。
因为所有ONC RPC报文要标识远程程序、该程序的版本号以及程序中的某个远程过程,这就有可能使程序从某个远程过程的一个版本从容的迁移到另一个版本上,而且可以在过程的旧版本继续运行的情况下测试服务器的新版本。
远程程序中的互斥
ONC RPC机制说明,在给定时刻,最多可以调用远程程序中的一个远程过程。因此,在给定的远程程序中,RPC自动提供了过程间的互斥。对于那些维护着被多个过程共享的数据的远程程序来说,这种互斥是重要的。比如含有用于insert和delete操作的远程过程的远程数据库程序中,程序员不必担心这两个远程过程会相互影响,因为RPC机制只允许一次执行一个调用。系统在当前调用完成之前,会阻塞其他的调用。即
ONC RPC提供了在单个远程程序中各个远程过程间的互斥;在远程程序中,一次最多可以执行一个远程过程调用
通信语义
在选择ONC RPC的语义是,设计者必须在两种可能中进行选择。一方面,为尽量使远程过程调用的行为像本地过程调用,RPC应使用一种像TCP这样可靠的传输,而且应该对程序员提供可靠性。远程过程调用机制应当把要么把调用过程传递到远程过程而且接受应答,要么报告通信无法进行。另一方面,为允许程序员使用高效率的、无连接的传输协议,远程过程调用机制应当支持用UDP这样的数据包协议进行通信。
ONC RPC并不强迫使用可靠语义。它允许每个应用程序选择用TCP还是UDP作为传输协议。此外,该标准并没有指明为达到可靠交付所要附加的协议或机制,而是把RPC语义作为下层的传输协议语义的一个功能。比如,由于UDP允许数据报丢失或重复,RPC指明,使用UDP的远程过程调用也会丢失或重复。
至少一次语义
ONC RPC以一种最简单的方式定义了远程过程调用的语义,它指明程序在任何交互中应当以最坏的可能做基础。比如,当使用UDP时,请求或者应答报文(调用远程过程或从远程过程返回)可能丢失或重复,如果远程过程没有返回,调用者不能认为远程过程没有被调用,这是因为即使请求没有丢失,应答也是可能被丢失的。如果远程过程返回了,调用者可以认为远程过程被调用了至少一次。
ONC RPC标注能用术语—-至少一次语义来描述RPC在执行时其调用者接收了一个应答,此标准还使用0或者多次语义来描述调用者没有收到应答时的一个远程过程调用的行为。
RPC的0或者多次语义意味着程序员选择UDP作为ONC RPC应用的传输协议时,所构造的程序必须能容忍0或者多次执行语义
在实际中,0或者多次语义意味着程序员要使每个过程调用是幂等的(重复调用会产生相同的结果)
RPC重传
伴随ONC PRC实现一起提供的库软件包含一种简单的超时和重传策略,但它并不保证严格意义下的可靠性、默认的超时机制是以固定的(非自适应)超时机制和固定的重传次数实现的。当RPC库发送报文时(对应于一次远程调用),它就启动计时器。如果计算器在响应到达前期满,软件就重发请求。程序员可以为某个定义应用调整超时时间和重试限制,但是该软件不能自适应网络延时
但是,简单的重传机制不能保证可靠性,也不能保证发起调用的应用程序可以得出关于远程过程执行情况的正确结论。比如,如果网络丢失了所有的响应,调用者可以重传几次请求,而每次请求的结构都是使远程过程执行一次。最终调用者的机器中库软件达到它的重试限制,于是宣布远程调用不能执行。更重要的是,应用程序不能将失败解释为远程过程从来没有执行过(实际上,它可能执行了很多次)
将远程程序映射到协议端口
UDP和TCP传输协议使用16比特的协议端口号来标识通信端点。而ONC RPC使用32比特数来标识远程程序,这种表示可能会超出协议端口的范围。因此,不可能将RPC程序号直接映射到协议端口号。更重要的是,并不是所有的RPC程序都能被分配一个唯一的协议端口号。
尽管RPC程序的潜在数量超过了分配熟知端口的能力,但RPC与其他服务没有什么特别的不同。在任意给定时间,单台计算机仅仅执行少量的远程程序。因此,只要端口分配是临时的,每个RPC程序可以获得一个协议端口号,并且使用这个端口号进行通信。
如果RPC程序没有使用保留的、熟知的协议端口,客户就不能直接与之联系。原因:
- 当服务器(远程程序)开始执行时,它要求操作系统分配一个未使用的协议端口号。服务器使用这个新分配的协议端口进行所有的通信。对服务器的每次启动,系统都有可能选择不同的协议端口,也就是说服务器可能被分配不同的端口
- 客户(发起远程调用的程序)知道它所希望与之联系的远程程序的机器地址以及RPC程序号。然而,因为RPC程序(服务器)只有在它开始执行之后采获得协议端口,客户就不能知道服务器获得了哪个协议端口。因此,客户不能直接联系远程程序
动态端口映射
为解决端口标识问题,客户必须能够在其启动时将RPC程序号和机器地址映射为目的机器中服务器所获得的协议端口。这种映射必须是动态的,因为如果机器重启或者RPC程序重启,端口可能被改变
为允许客户联系远程程序,ONC RPC机制还包括动态映射服务。提供RPC服务(即运行服务器)的每台机器维护这一个端口映射数据库,而且还提供了一种允许调用者将RPC程序员映射为协议端口的机制。RPC端口映射机制是这样的,它在每台机器中用一个服务器维护一个小数据库,这个服务器称为RPC端口映射器(RPC port mapper)。下图展示了作为一个单独的服务器进程运行的端口映射器
RPC端口映射器算法
算法如下(每个实现RPC程序的服务器的机器都要运行端口映射服务器):
- 创建被动套接字,将其绑定到分配给ONC RPC端口映射器的熟知端口111上(这样调用者才能联系端口映射器)
- 重复的接收请求,以注册RPC程序号,或者在给定某一RPC程序号时查找其协议端口。
- 注册请求来自与端口映射器同一机器的RPC程序。每个注册请求指明了一个三元组,它由RPC程序号、版本号、以及当前用于到达该程序的协议端口号组成。当注册请求到达时,端口映射器将这个三元组加到它的映射数据库中
- 查找请求来自任意机器。每一个查找请求都指明了远程程序号和版本号,并且请求知道用于达到这个程序的协议端口号。端口映射器将在其数据库中查找这个远程程序,并响应这个程序的协议端口
从上知道,调用者必须要知道执行远程程序机器的地址、分配给该程序的RPC程序号以及版本号。
ONC RPC的报文格式
不想很多TCP/IP族协议一样,ONC RPC没有固定的报文格式。协议标准使用一种称为XDR的语言定义RPC报文的一般格式以及每个字段中的数据条目。一般来说,该语言指明应该如何装配成报文的一系列数据条目。每个条目都用XDR表示标准来编码
RPC报文首部中的报文类型字段区分两种类型的报文,这两种类型是客户用来发起远程过程调用的报文和远程过程用来进行应答的报文。报文类型字段中所使用的的常量可以用XDR语言定义。比如:
enum msg_type{ // RPC message type constants
CALL = 0;
REPLY = 1;
};
它声明了符号常量CALL和REPLY是枚举类型msg_type中的值。
XDR语言中的数据结构可以看作是XDR类型的序列,它可以被解释为指令,该指令用于对报文进行装配,装配的方法是用XDR构成数据。比如,一旦为符号常量声明了值,XDR语言可以定义RPC报文的格式:
struct rpc_msg{ // format of an RPC message
unsigned int mesgid; //used to match reply to call
union switch(mst_type mesgt){
case CALL:
call_body cbody;
case REPLY:
repy_body rbody;
}body;
}
这个声明说明了RPC报文-------rpc_msg,该报文由一个整数型报文标识符mesgid和后面跟随者的用XDR表示的不同union组成。使用XDR表示,每个联合由一个整数开始,在这里是mesgt。mesgt决定了RPC报文中剩下部分的格式:它包含一个值,该值定义消息要么是一个CALL,要么是一个REPLY。CALL报文以call_body的形式包含了更进一步的信息。REPLY报文以rply_body的形式包含了更进一步的信息。对call_body和rply_body的声明必须在其他地方给出。比如:
struct call_body{ // format of RPC CALL
unsigned int rpcvers; // which version of RPC
unsigned int rprog; //number of remote program
unsigned int rprogvers; // version number of remote prog
unsigned int rproc; // number of remote procedure
opaque_auth cred; //credentials for called auth
opaque_auth verf; // authentication verifier
/*ARGC*/ // arguments for remote proc
}
这个远程过程调用体(body)的前面几行没有什么特别的。调用者必须在字段rpcvers中提供RPC协议版本号,以便保证客户和服务器使用相同的报文格式。三个整形字段rprog、rprogvers和rproc分别指明要调用的远程程序、所期望的程序版本以及在这个程序中的远程过程。cred和verf含有被调用程序用于鉴别调用者身份的信息
对远程过程进行参数排序
在RPC报文中,鉴别信息之后的一些字段含有远程过程的参数。参数的个数以及每个参数的类型取决于被调用的远程过程。
RPC必须将它的所有参数表示为一种外部的形式,这种形式允许参数在计算机之间传递。具体来说,如果传递给远程过程的任何参数由一个像链表那样的复杂数据结构组成,那么它必须被编码为一种可通过网络发送的压缩形式。我们用术语排序、线性化或者串行化来代表这种读参数编码的工作。我们说,RPC的客户一端将参数排序到报文中,在服务器一端再将它们反排序。程序员必须记住,尽管RPC允许RPC调用包含有复杂的数据对象,排序或者反排序大的数据结构可能要求大量的CPU时间和网络带宽。因此,多数程序员避免把链表的结构作为参数来传递
鉴别
RPC定义了多个可能的鉴别形式,包括一个依赖于操作系统提供功能的简单的鉴别体制和一个使用数据加密标准DES的更复杂的体制。鉴别信息可以是下面四种类型之一:
enum auth_type{
AUTH_NULL = 0; //no authentication
AUTH_UNIX = 1; //mathine name authentic
AUTH_SHORT = 2; // used for short from auth in messages after the first
AUTH_DES = 3; //NTST's DES standard
};
对于每种情况,RPC把鉴别信息的格式和解释留给了鉴别子系统。因此,RPC报文中鉴别结构的声明使用了关键字opaque,以此指出它出现在报文中,但不要任何解释
struct opaque_auth{ // structure for authent.info
auth_type atype; //which type of authentication
opaque body<400>; //data for the type specified
};
当然,每种鉴别方法都使用一种特定的格式来编码数据。比如,多数发送RPC报文的网点都使用各种版本的Unix操作系统。Unix鉴别定义其鉴别信息的结构要包含几个字段:
Unix鉴别依赖于客户机器在字段smachine中提供它的名字。在字段userid中给出发出请求的用户数值标识符。客户需要在字段timestamp中指明它的本地时间,该值可以用来对请求排序。最后,客户在字段grpid和grpids中填入发送用户的主数字组标识符和辅助标识符
总结
远程过程模式把每个服务器看成是一个或者多个过程的实现。
使用远程过程模型有助于程序员关注于应用而不是通信协议。程序员可以构建和测试某个具体问题的常规程序,接着将这个程序划分为几个部分,这些部分在两台或者多台计算机上执行。
Sun公司定义了远程过程调用的具体形式,现已经称为事实上的标准。ONC RPC为表示远程过程指明了一种体制,还为RPC报文格式指定了一个标准。这个标准为使报文表示独立于机器而使用了外部数据表示XDR
ONC RPC程序并不像传统的客户-服务器程序那样使用了熟知协议端口,而是使用了一种动态绑定机制,这种机制允许每个RPC程序在启动时选择一个任意的、未使用过的协议端口。这种绑定机制还要求每台提供RPC程序的计算机在熟知协议端口上运行端口映射服务器。每个RPC程序在获得了协议端口后之后需要向其本地机器中的端口映射器注册。当某个RPC程序想与某个RPC程序联系时,它首先要联系目标机器中的端口映射器。端口映射器在响应中告诉客户目标RPC程序使用了哪个端口。客户一旦获得了目标RPC程序所使用的正确协议端口号,就可以使用这个端口直接与目标RPC程序进行联系