什么是RPC框架
RPC(Remote Procedure Call),即远程过程调用,主要是让构建分布式计算更容易,透明。
要理解远程过程调用,那么需要首先明白本地过程调用做了什么:
本地调用
int multiply(int l, int r)
{
int y = l * r;
return y;
}
int lvalue = 10;
int rvalue = 20;
int l_timers_r = multiply(lvalue, rvalue);
1、将 lvalue 和 rvalue 的值压栈
2、进入Multiply函数,取出栈中的值10 和 20,将其赋予 l 和 3、执行第2行代码,计算 l * r ,并将结果存在 y
4、将 y 的值压栈,然后从Multiply返回
5、第8行,从栈中取出返回值 200 ,并赋值给 l_times_r
远程过程调用
在远程过程调用的时,我们需要执行的函数体是在远程的机器上的,也就是说,multiply 是在另一个进程中执行的,这就带来几个问题:
-
Call ID 映射
怎么告诉远程机器我们要调用multiply
,而不是add
?
在本地调用中,函数体是直接通过函数指针来指定的,但是在远程调用中,函数指针是不行的,因为两个进程的地址空间是完全不一样的。
所以,在RPC中,所有的函数都必须有自己的一个ID。这个ID在所有进程中都是唯一确定的。客户端在做远程过程调用时,必须附上这个ID。然后我们还需要在客户端和服务端分别维护一个函数 <--> Call ID
的对应表。
当客户端需要进行远程调用时,它就查一下这个表,找出相应的Call ID,然后把它传给服务端,服务端也通过查表,来确定客户端需要调用的函数,然后执行相应函数的代码。 -
序列化和反序列化
客户端怎么把参数值传给远程的函数呢?在本地调用中,我们只需要把参数压到栈里,然后让函数自己去栈里读就行。
但是在远程过程调用时,客户端跟服务端是不同的进程,不能通过内存来传递参数。甚至有时候客户端和服务端使用的都不是同一种语言。这时候就需要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成自己能读取的格式。这个过程叫序列化和反序列化。同理,从服务端返回的值也需要序列化反序列化的过程。 -
网络传输
远程调用往往用在网络上,客户端和服务端是通过网络连接的。所有的数据都需要通过网络传输,因此就需要有一个网络传输层。网络传输层需要把Call ID和序列化后的参数字节流传给服务端,然后再把序列化后的调用结果传回客户端。只要能完成这两者的,都可以作为传输层使用。因此,它所使用的协议其实是不限的,能完成传输就行。尽管大部分RPC框架都使用TCP协议,但其实UDP也可以,而gRPC干脆就用了HTTP2
所以一个简单的RPC框架,最基础的要有3大部分:
- Call ID映射可以直接使用函数字符串,也可以使用整数ID。映射表一般就是一个哈希表。
- 序列化反序列化:如Protobuf或者FlatBuffers之类的。
- 网络传输库:可以自己写socket,或者用asio,ZeroMQ,Netty之类。
大型分布式RPC一般需要解决:
- protocol:传输协议
- proxy:client代理,服务引用方调用方法通过代理发送远程消息
- codec:协议编解码压缩等
- transport:协议传输
- registry:注册中心,服务注册服务发现
- cluster:负载均衡,服务容错策略
- 其他:服务降级,服务隔离,服务治理
HTTP 和 RPC
RPC 能做的,一般HTTP 请求同样也能做到相同功能。那为什么还需要RPC呢?
HTTP协议,以其中的Restful规范为代表,其优势很大。
- 可读性好
- 可以得到防火墙的支持
- 跨语言的支持
但是HTTP也有其缺点,这是与其优点相对应的:
- 有用信息占比少,毕竟HTTP工作在第七层,包含了大量的HTTP头等信息。
两者之间的关系,可以用:
image.png
参考资料
1、https://www.zhihu.com/search?type=content&q=rpc
2、https://www.zhihu.com/question/25536695
3、https://www.zhihu.com/question/41609070/answer/1030913797
网友评论