第五届青训营 」伴学笔记创作活动的第 15 天
最近在学习go语言的微服务部分,因此特地对RPC进行一个归纳总结。
什么是RPC
RPC(Remote Procedure Call),即远程过程调用。它允许像调用本地服务一样调用远程服务。
RPC是一种服务器-客户端(Client/Server)模式,经典实现是一个通过发送请求-接受回应进行信息交互的系统。
首先与RPC(远程过程调用)相对应的是本地调用。
本地调用
package main
import "fmt"
func add(x, y int)int{
return x + y
}
func main(){
// 调用本地函数add
a := 10
b := 20
ret := add(x, y)
fmt.Println(ret)
}
将上述程序编译成二进制文件——app1后运行,会输出结果30。
在app1程序中本地调用add函数的执行流程,可以理解为以下四个步骤。
- 将变量 a 和 b 的值分别压入堆栈上
- 执行 add 函数,从堆栈中获取 a 和 b 的值,并将它们分配给 x 和 y
- 计算 x + y 的值并将其保存到堆栈中
- 退出 add 函数并将 x + y 的值赋给 ret
RPC调用
本地过程调用发生在同一进程中——定义add函数的代码和调用add函数的代码共享同一个内存空间,所以调用能够正常执行。
但是我们无法直接在另一个程序——app2中调用add函数,因为它们是两个程序——内存空间是相互隔离的。(app1和app2可能部署在同一台服务器上也可能部署在互联网的不同服务器上。)
RPC就是为了解决类似远程、跨内存空间、的函数/方法调用的。要实现RPC就需要解决以下三个问题。
- 如何确定要执行的函数? 在本地调用中,函数主体通过函数指针函数指定,然后调用 add 函数,编译器通过函数指针函数自动确定 add 函数在内存中的位置。但是在 RPC 中,调用不能通过函数指针完成,因为它们的内存地址可能完全不同。因此,调用方和被调用方都需要维护一个{ function <-> ID }映射表,以确保调用正确的函数。
- 如何表达参数? 本地过程调用中传递的参数是通过堆栈内存结构实现的,但 RPC 不能直接使用内存传递参数,因此参数或返回值需要在传输期间序列化并转换成字节流,反之亦然。
- 如何进行网络传输? 函数的调用方和被调用方通常是通过网络连接的,也就是说,function ID 和序列化字节流需要通过网络传输,因此,只要能够完成传输,调用方和被调用方就不受某个网络协议的限制。.例如,一些 RPC 框架使用 TCP 协议,一些使用 HTTP。