什么是远程过程调用(RCP),远程过程调用服务实现原理详解
远程过程调用,简称 RPC,是一种最为常见的远程服务。RPC 对于通过网络连接系统之间的过程调用进行了抽象。它在许多方面都类似于 IPC 机制,并且通常建立在 IPC 之上。不过,因为现在的情况是进程处在不同系统上,所以应提供基于消息的通信方案,以提供远程服务。
与 IPC 的消息不一样,RPC 通信交换的消息具有明确结构,不再仅仅是数据包。消息传到 RPC 服务,RPC 服务监听远程系统的端口号;消息包含用于指定执行函数的一个标识符以及传递给函数的一些参数。然后,函数按要求来执行,而所有结果会通过另一消息,传递回到请求者。
端口只是一个数字,处于消息分组头部。虽然每个系统通常只有一个网络地址,但是对于这个地址它有许多端口号,以便区分所支持的多个网络服务。如果一个远程进程需要服务,那么它向适当端口发送消息。
例如,如果有个系统允许其他系统列出当前用户,那么它可以有一个支持这个的 RPC 服务,该服务会监听某个端口,如 3027。任何一个远程系统如要得到所需信息(即列出当前用户),只要向服务器端口 3027 发送一个 RPC 消息,就能通过回复消息收到数据。
RPC 语义允许客户调用位于远程主机的过程,就如调用本地过程一样。通过客户端提供的存根,RPC 系统隐藏通信细节。
通常,对于每个单独远程过程,都有一个存根。当客户调用远程过程时,RPC 系统调用适当存根,并且传递远程过程参数。这个存根定位服务器的端口,并且封装参数(打包参数),以便通过网络传输。然后,存根通过消息传递,向服务器发送一个消息。
服务器的类似存根收到这个消息,并且调用服务器的过程。如果必要,返回值可通过同样技术传回到客户机。对于 Windows 系统,编译由 MIDL 语言编写的规范,可以生成存根代码。Microsoft 接口定义语言用于定义客户机与服务器之间的接口。
有一个必须处理的事项,涉及如何处理客户机和服务器系统的不同数据表示。考虑 32 位整数的表示。有的系统使用内存的高地址,以存储高位字节(称为大端结尾),而其他系统使用内存的高地址,以存储低位字节(称为小端结尾)。没有哪种顺序“更好”;这是由计算机体系结构来选择的。
为了解决这一差异,许多 RPC 系统定义一个独立于机器的数据表示。一种这样的表示称为外部数据表示(简称XDR)。在客户端,参数封装将机器相关数据打包成 XDR,再发送到服务器。在服务器端,XDR 数据被分封,再转成机器相关数据以交给服务器。
另外一个重要事项涉及调用语义。虽然本地过程调用只在极端情况下才失败,但是由于常见网络错误,RPC 可能执行失败或者多次重复执行。解决这个问题的一种方法是:操作系统确保每个消息执行正好一次,而非执行最多一次。大多数本地过程调用具有“正好一次”的特点,但是实现更难。
首先,考虑“最多一次”,可以通过为每个消息附加时间戳来实现。服务器对所处理的消息应有一个完整的或足够长的时间戳的历史,以便确保能够检测到重复消息。进来的消息,如果其时间戳已出现过,则被忽略。这样,客户能够一次或多次发送消息,并确保仅执行一次。
对于“正好一次”,需要消除服务器从未收到请求的风险。为了做到这点,服务器必须执行前面所述的“最多一次”的协议,但是也必须向客户确认 RPC 调用已经收到并且已经执行。这些 ACK(确认)消息在网络中是常见的。客户机应周期性地重发每个 RPC 调用,直到它接收到对该调用的 ACK。
然而,另外一个重要问题涉及服务器和客户机之间的通信。对于标准的过程调用,链接、加载或执行有一定形式的绑定,以便过程名称被过程的内存地址所替代。RPC 方案也要有一个类似于客户机和服务器端口之间的绑定,但是客户机如何知道服务器上的端口呢?这两个都没有对方的完全信息,因为它们并不共享内存。有两种方法是常见的。
第一种方法,绑定信息可以按固定的端口地址形式预先固定。在编译时,RPC 调用有一个与它关联的固定端口。一旦程序编译后,服务器无法更改请求服务的端口号。
第二种方法,绑定通过交会机制动态进行。通常,操作系统在一个固定 RPC 端口上,提供交会服务程序或月老。客户程序发送一个包括 RPC 名称的消息到交会服务程序,以便请求所需执行 RPC 的端口地址。在得到返回的端口号后,RPC 调用可以发送到这一端口号,直到进程终止(或服务器崩溃)。
这种方式的初始请求需要额外开销,但是比第一种更灵活。图 1 为这种交互的一个实例。
图 1 远程过程调用(RPC)的执行
RPC 方案可用于实现分布式文件系统。这种系统可以通过一组 RPC 服务程序和客户来实现。当要进行文件操作时,消息可以发到服务器的分布式文件系统的端口。该消息包括要执行的磁盘操作。
与 IPC 的消息不一样,RPC 通信交换的消息具有明确结构,不再仅仅是数据包。消息传到 RPC 服务,RPC 服务监听远程系统的端口号;消息包含用于指定执行函数的一个标识符以及传递给函数的一些参数。然后,函数按要求来执行,而所有结果会通过另一消息,传递回到请求者。
端口只是一个数字,处于消息分组头部。虽然每个系统通常只有一个网络地址,但是对于这个地址它有许多端口号,以便区分所支持的多个网络服务。如果一个远程进程需要服务,那么它向适当端口发送消息。
例如,如果有个系统允许其他系统列出当前用户,那么它可以有一个支持这个的 RPC 服务,该服务会监听某个端口,如 3027。任何一个远程系统如要得到所需信息(即列出当前用户),只要向服务器端口 3027 发送一个 RPC 消息,就能通过回复消息收到数据。
RPC 语义允许客户调用位于远程主机的过程,就如调用本地过程一样。通过客户端提供的存根,RPC 系统隐藏通信细节。
通常,对于每个单独远程过程,都有一个存根。当客户调用远程过程时,RPC 系统调用适当存根,并且传递远程过程参数。这个存根定位服务器的端口,并且封装参数(打包参数),以便通过网络传输。然后,存根通过消息传递,向服务器发送一个消息。
服务器的类似存根收到这个消息,并且调用服务器的过程。如果必要,返回值可通过同样技术传回到客户机。对于 Windows 系统,编译由 MIDL 语言编写的规范,可以生成存根代码。Microsoft 接口定义语言用于定义客户机与服务器之间的接口。
有一个必须处理的事项,涉及如何处理客户机和服务器系统的不同数据表示。考虑 32 位整数的表示。有的系统使用内存的高地址,以存储高位字节(称为大端结尾),而其他系统使用内存的高地址,以存储低位字节(称为小端结尾)。没有哪种顺序“更好”;这是由计算机体系结构来选择的。
为了解决这一差异,许多 RPC 系统定义一个独立于机器的数据表示。一种这样的表示称为外部数据表示(简称XDR)。在客户端,参数封装将机器相关数据打包成 XDR,再发送到服务器。在服务器端,XDR 数据被分封,再转成机器相关数据以交给服务器。
另外一个重要事项涉及调用语义。虽然本地过程调用只在极端情况下才失败,但是由于常见网络错误,RPC 可能执行失败或者多次重复执行。解决这个问题的一种方法是:操作系统确保每个消息执行正好一次,而非执行最多一次。大多数本地过程调用具有“正好一次”的特点,但是实现更难。
首先,考虑“最多一次”,可以通过为每个消息附加时间戳来实现。服务器对所处理的消息应有一个完整的或足够长的时间戳的历史,以便确保能够检测到重复消息。进来的消息,如果其时间戳已出现过,则被忽略。这样,客户能够一次或多次发送消息,并确保仅执行一次。
对于“正好一次”,需要消除服务器从未收到请求的风险。为了做到这点,服务器必须执行前面所述的“最多一次”的协议,但是也必须向客户确认 RPC 调用已经收到并且已经执行。这些 ACK(确认)消息在网络中是常见的。客户机应周期性地重发每个 RPC 调用,直到它接收到对该调用的 ACK。
然而,另外一个重要问题涉及服务器和客户机之间的通信。对于标准的过程调用,链接、加载或执行有一定形式的绑定,以便过程名称被过程的内存地址所替代。RPC 方案也要有一个类似于客户机和服务器端口之间的绑定,但是客户机如何知道服务器上的端口呢?这两个都没有对方的完全信息,因为它们并不共享内存。有两种方法是常见的。
第一种方法,绑定信息可以按固定的端口地址形式预先固定。在编译时,RPC 调用有一个与它关联的固定端口。一旦程序编译后,服务器无法更改请求服务的端口号。
第二种方法,绑定通过交会机制动态进行。通常,操作系统在一个固定 RPC 端口上,提供交会服务程序或月老。客户程序发送一个包括 RPC 名称的消息到交会服务程序,以便请求所需执行 RPC 的端口地址。在得到返回的端口号后,RPC 调用可以发送到这一端口号,直到进程终止(或服务器崩溃)。
这种方式的初始请求需要额外开销,但是比第一种更灵活。图 1 为这种交互的一个实例。
图 1 远程过程调用(RPC)的执行
RPC 方案可用于实现分布式文件系统。这种系统可以通过一组 RPC 服务程序和客户来实现。当要进行文件操作时,消息可以发到服务器的分布式文件系统的端口。该消息包括要执行的磁盘操作。
磁盘操作可能是 read(读)、write(写)、rename(重命名)、delete(删除)或status(状态),对应于通常的文件相关的系统调用。
返回消息包括来自调用的任何数据,这个调用是由 DFS(分布式文件系统)服务程序代表客户所执行的。例如,一个消息可能包括一个传输整个文件到客户机的请求,或仅限于一个简单的块请求。对于后者,如果需要传输整个文件,可能需要多个这样的请求。所有教程
- socket
- Python基础教程
- C#教程
- MySQL函数
- MySQL
- C语言入门
- C语言专题
- C语言编译器
- C语言编程实例
- GCC编译器
- 数据结构
- C语言项目案例
- C++教程
- OpenCV
- Qt教程
- Unity 3D教程
- UE4
- STL
- Redis
- Android教程
- JavaScript
- PHP
- Mybatis
- Spring Cloud
- Maven
- vi命令
- Spring Boot
- Spring MVC
- Hibernate
- Linux
- Linux命令
- Shell脚本
- Java教程
- 设计模式
- Spring
- Servlet
- Struts2
- Java Swing
- JSP教程
- CSS教程
- TensorFlow
- 区块链
- Go语言教程
- Docker
- 编程笔记
- 资源下载
- 关于我们
- 汇编语言
- 大数据
- 云计算
- VIP视频