RPC 框架
客户端的应用发起一个远程调用时,实际是通过本地调用 Stub。Stub 负责将调用的接口、方法和参数,通过约定的协议规范进行编码,并通过本地的 RPCRuntime 进行传输,将网络包发送到服务器。
服务器的 RPCRuntime 收到请求,交给提供方 Stub 解码,然后调用服务端的方法,服务端执行方法,返回结果。提供方 Stub 将结果编码,发送各客户端。
客户端的 RPCRuntime 收到结果,发给调用方 Stub 解码,返回给客户端。
RPC 实现
Sun 公司是第一个提供商业化 RPC 库和 RPC 编译器的公司,其 RPC 框架在 NFS(Network File System,网络文件系统)协议中使用。
NFS 运行时,启动两个服务端:
- mountd,挂载文件路径,将一个远程的目录 mount 到本地目录。
- nfsd,读写文件,在挂载的目录中写入、读出任何文件时,其实操作的是远程另一台机器上的文件。
RPC 的底层是 Socket。
RPC 调用过程中,所有数据类型都要封装成 XDR 格式。XDR(External Data Representation,外部数据表示法)是一个标准的数据压缩格式,可以表示基本的数据类型,也可表示结构体。
XDR 数据类型:
RPC 的调用和结果返回的格式:
- XID 唯一标识一对请求和回复,请求为 0,回复为 1。
- RPC 有版本号,两端不匹配 RPC 版本号,返回 Deny,原因为 RPC_MISMATCH。
- 程序有编号,找不到该程序,返回 PROG_UNAVALI。
- 程序有版本号,版本号不匹配,返回 PROG_MISMATCH。
- 程序中有多个方法,方法有编号,找不到则返回 PROC_UNAVAIL。
- 调用需要认证授权,不通过则 Deny。
- 参数列表,无法解析则返回 GABAGE_ARGS。
客户端和服务端在实现 RPC 时,首先要定义一个双方都认可的程序、版本、方法、参数等。
图中,对于加法函数,双方约定一个协议定义文件,同理,如果时 NFS、mount 和读写,也会有类似定义。
ONC RPC 会提供一个工具,根据协议定义文件生成客户端和服务端的 Stub 程序。
最下层为 XDR 文件,对参数进行编码和解码。
客户端调用 clnt_create 创建一个连接,然后调用 add_1(Stub 函数),如同本地调用,该函数发起 RPC 调用,通过调用 clnt_call 调用 ONC RPC 类库发送真正的请求。
服务端 Stub 监听客户请求,如果是 add,调用真正的服务端逻辑,即将两数相加。
服务端将结果返回服务端 Stub,Stub 将结果发送给客户端,客户端 Stub 将结果返回给客户端应用程序。
传输问题
错误、重传、丢包、性能等问题,由 ONC RPC 类库解决。
为解决传输问题,对于每个客户端,都会创建一个传输管理层,会有队列机制、拥塞窗口机制等。
网络传输时,经常需要等待,同步方式效率较低,为了能异步处理,往往通过状态机实现远程调用。不满足状态时,不等,而是将资源留出,供其他 RPC 调用。
- 进入起始状态,查看 RPC 的传输队列中是否满,如果队列满,则直接结束或重试;否则,申请成功,可分配内存,获取服务的端口号,连接服务器。
- 连接成功,发送 RPC 请求,等待获取 RPC 结果;发送出错,可重新发送;连接断开,可重新连接;超时,可重新传输;获取到结果,则解码,正常结束。
服务发现问题
在 ONC RPC 中,服务发现通过 portmapper 实现。
portmapper 在一个众所周知的端口上启动。RPC 程序启动时,向 portmapper 注册自己的端口。客户端要访问 RPC 服务端时,先查询 protmapper,获取 RPC 服务端的端口,然后向该端口建立连接,开始 RPC 调用。图中为 mount 命令的 RPC 调用实现。