操作系统:远程过程调用( Remote Procedure Call,RPC)
目录
什么是 RPC?
为什么需要 RPC?
RPC 中的消息
RPC 消息的结构
这些消息发送到哪里去?
RPC 是如何“伪装成”本地函数调用的?
Stub:伪装函数的“代理人”
什么是参数封装(Parameter Marshalling)?
我们现在来讲解一个在分布式系统中非常重要的通信机制:远程过程调用 Remote Procedure Call(RPC)
什么是 RPC?
Remote Procedure Call(RPC) 是一种协议,允许一个程序调用另一个远程计算机上的函数或服务,就好像它是本地函数一样,而无需了解网络通信的细节。
RPC 的核心思想
虽然这个函数其实在另一台机器上,你仍然可以用和本地一样的方式去调用它。
RPC 做的事情可以简单地说是:不只是传数据,而是“远程执行一段代码”,并把结果带回来。
它的重点是:
-
调用远程的“函数”或“服务”,而不仅仅是发一个数据包;
-
你不需要关心它是不是在另一台机器上;
-
系统帮你把调用过程变成“发送请求 → 执行函数 → 发送结果”的一套流程。
为什么需要 RPC?
一、先从我们熟悉的「打函数」说起
当你写程序的时候,比如调用一个加法函数:
result = add(3, 5)
你会发现:
-
你只写了一句;
-
函数在哪?在本地;
-
参数怎么传?系统帮你传了;
-
结果怎么回来?系统帮你拿回来了。
这就叫做本地函数调用:你只管调用,系统自动把一切“底层操作”都做好了。
二、再看看「进程之间怎么交流」(IPC)
有时候,两个程序(进程)需要互相传数据,比如:
-
你运行的浏览器想从另一个本地程序拿图像;
-
一个程序做运算,另一个来展示结果。
这就用到了进程间通信(IPC),比如:
-
A 进程用
send()
发一个消息; -
B 进程用
receive()
接收消息; -
两个程序必须自己约定:这个消息是什么意思,内容要怎么解析。
简单来说:
IPC 更像是“写信”交流,你发一个东西,我收一个东西。大家要约定好格式、理解内容,通信本身是“传数据”而不是“请求功能”。
三、现在来看看 RPC(远程过程调用)
好,现在问题来了:如果这个函数不在本地,而是在另一台电脑上怎么办?
比如你电脑上运行了一个程序,它想调用远程服务器上的函数。
你能不能像调用本地一样写一句:
result = remote_add(3, 5)
这时候,RPC 就派上用场了。
RPC 让你调用远程的函数,就像在本地调用一样。你不用自己写通信代码,不用关心网络传输细节。
IPC 是在“传数据”,RPC 是在“请求帮我做事”。
RPC 把通信和函数调用结合起来,隐藏了底层复杂性,是写分布式程序非常常用的方式。
类比 | 说明 |
---|---|
IPC 像是写信 | 你写信告诉对方一些内容(发送数据),对方自己想办法理解 |
RPC 像是打电话 | 你直接打电话说“帮我做这件事”,对方立即照做,然后告诉你结果 |
RPC 与 IPC 的关系和区别
项目 | IPC(进程间通信) | RPC(远程过程调用) |
---|---|---|
通信范围 | 同一台主机的进程 | 不同主机的进程 |
通信形式 | 数据共享、消息传递等 | 函数调用封装为消息 |
通信协议 | 本地内核机制(管道、共享内存) | 网络协议(通常是 TCP/UDP) |
消息内容 | 可能是任意格式数据 | 结构化:包含函数名和参数 |
操作系统:进程间通信( Interprocess Communication,简称 IPC)-CSDN博客
RPC 中的消息
回顾一下:IPC 的消息是什么样的?
在传统的 IPC(进程间通信)中,消息就是一段“原始数据”,比如一个字符串、一个数字数组,或者某种格式的字节流。
"ADD 3 5"
这是程序 A 发给程序 B 的一句话,但程序 B 得自己想办法理解这句话的含义,比如:
-
它是不是“加法”命令?
-
后面两个数是什么意思?
-
执行后结果要不要回给 A?
这完全靠开发者来设计和解释消息内容,而RPC 的消息是结构化的、有标准格式的。
RPC 消息的结构
RPC 系统为了让通信过程更像“调用函数”,而不是“发信息”,它设计了一套清晰的结构,让每条消息都包含下面这些内容:
-
函数标识符(Function ID):客户端要调用哪个函数;
-
参数数据(Arguments):传递给远程函数的参数;
-
调用标识(Call ID):请求编号,用于匹配返回值;
-
返回值(在响应中):函数执行的结果;
这使得 RPC 不再只是“传数据”,而是明确表达“请求执行一个功能,并返回结果”。
字段 | 内容说明 |
---|---|
函数名 | 要执行的远程函数名称,例如 add() 、get_user() |
参数值 | 函数的参数,例如 3, 5 或 user_id=42 |
唯一标识符 | 每个请求有编号,方便匹配返回结果 |
返回通道信息 | 哪个端口/地址返回结果 |
这意味着——RPC 的消息不再是“信封里写一堆字”,而是:
自动生成的、机器能看懂的函数调用说明书。
这些消息发送到哪里去?
每个运行 RPC 服务的远程主机上,通常有一个叫做 RPC daemon(守护进程) 的后台程序在监听某个端口(比如 1234 端口)。
当客户端发出 RPC 请求时,这条结构化消息会:
-
通过网络发给目标主机;
-
交给监听端口的 RPC 守护进程;
-
它读取消息,知道:
-
要调用哪个函数
-
参数是什么
-
-
然后在本地真正去执行这个函数;
-
把返回结果再次打包成消息,发送回客户端。
阶段 | 内容 |
---|---|
客户端发送请求 | 消息中包含函数名、参数等 |
服务器端接收 | 本地 RPC 服务进程(daemon)监听某个端口,解析消息 |
执行远程函数 | 找到匹配的函数,执行传入的参数逻辑 |
返回结果 | 服务器打包返回值,发送回客户端 |
网络中的 RPC 通信流程图:
Client Network Server
-------- --------- --------
Call fn(x, y) ─────▶ 封装消息包 ─────▶ RPC Daemon 解析⬇ ⬇Send to IP:port 查找 fn 并执行(x, y)⬆ ⬆接收返回值 ◀──── 回送结果消息 ◀──── 返回执行结果
在 RPC 中,消息是自动构造的、有结构的,明确表示“调用哪个函数、传什么参数”,不像 IPC 那样只是发一段原始数据。
这让 RPC 能够像本地函数调用一样稳定、可控,并且适合在网络上传输和执行。
RPC 是如何“伪装成”本地函数调用的?
RPC(远程过程调用)希望让你写代码时像这样:
result = remote_add(3, 5)
看起来就像是调用一个本地函数 add()
,但其实:
-
函数 真正执行是在另一台机器上;
-
数据是通过网络传过去的;
-
然后结果又从远程传回来。
那它是怎么实现这么“骗你”的效果呢?这就靠存根(Stub) 和 参数封装(Marshalling)。
Stub:伪装函数的“代理人”
Stub 是一个代理函数。你以为你调用的是远程函数,实际你调用的是一个“本地小助手”(Stub),它背后再去做真正的远程调用。
RPC 中有两个 Stub:
Stub 类型 | 位置 | 作用 |
---|---|---|
Client Stub | 客户端 | 模拟远程函数,接收参数、打包、发消息 |
Server Stub | 服务器端 | 解包参数、调用真正的函数,打包返回结果 |
你以为你在调用远程服务器的 add()
,其实你调用的是一个本地的“stub 函数”。
它不是真的运行 add(3, 5)
,而是做这几件事:
-
把函数名、参数等打包成一个消息;
-
通过网络发送给远程服务器;
-
等服务器执行完毕后,接收结果并返回给你。
这样你就“以为”是本地调用,实际上系统在偷偷做远程通信。
调用流程图
【客户端】
你写的代码:result = remote_add(3, 5)│▼Client Stub(代理函数)└── 参数打包成消息│▼网络发送(消息)【服务器】Server Stub(接收者)└── 解包参数│▼真正执行:add(3, 5)│▼得到结果:8└── 结果打包成消息│▼网络返回(消息)【客户端】Client Stub 接收并解包│▼把 8 返回给调用者
什么是参数封装(Parameter Marshalling)?
参数封装是把多个参数“打包成可以在网络上传输的数据”。
比如:
remote_add(3, 5)
本地函数接收的是两个整数。
但网络不能直接发送“函数+数字”,它只能传输一串数据(比如文本、二进制)。
所以 stub 要把调用请求变成一个结构化的消息:
-
把 3 和 5 变成字节流;
-
加上函数名标识(比如
"add"
); -
加上请求编号(为了匹配响应);
-
打包成一个结构清晰的消息。
这个过程就叫做 参数封装(Marshalling)。反过来,服务器也会用类似的方式把结果也打包发回去。
响应过程也是一样
-
远程服务器的 stub 接收到这个消息后解包;
-
调用真正的函数
add(3, 5)
; -
拿到结果 8,再“封装”成返回消息;
-
发送回客户端 stub;
-
客户端 stub 解包,把结果还给你的代码。
RPC 的核心就是通过:
-
Stub:代理函数封装请求
-
参数封装:打包参数成网络格式
-
消息传输:靠 socket 等机制发送请求和响应
来实现“隐藏通信细节”的魔法。