介绍
RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务。
本地过程调用
1 | def add(a, b): |
函数调用过程:
- 将1和2压入add函数的栈
- 进入add函数,从栈中取出1和2分别赋值给a和b
- 执行a + b将结果赋值给局部的total并压入栈
- 将栈中的值取出来赋值给全局的total
远程过程调用面临的问题
Call ID映射
我们怎么告诉远程机器我们要调用add,而不是sub或者foo呢?在本地调用中,函数体是直接通过函数指针来指定的,我们调用add,
编译器就自动帮我们调用它相应的函数指针。而在远程过程调用中,函数指针是不行的,因为两个进程的地址空间是完全不一样的。
所以,在RPC中,所有的函数都必须有自己的一个ID,这个ID在所有的进程中都是唯一确定的。客户端在做远程过程调用时,
必须附上这个ID,然后我们还需要在客户端和服务端分别维护一个(函数<–>Call ID)的对应表。两者的表不一定需要完全相同,
但相同的函数对应的Call ID必须相同。当客户端函数需要进行远程过程调用时,它就查一下这个表,找出相应的Call ID,
然后把它传给服务端,服务端也通过查表,来确定客户端需要调用的函数,然后执行相应函数的代码。序列化与反序列化
客户端如何把参数值传给远程的函数呢?在本地调用中,我们只需要把参数压到栈里,然后让函数自己去栈里读就行。
但在远程过程调用时,客户端跟服务端是不同的进程,不能通过内存来传递参数。甚至有时候客户端和服务端使用的都不是同一种语言
(比如服务端用C++,客户端使用Java或者Python)。这时候就需要客户端把参数先转成一个字节流,传给服务端,
再把字节流转成自己能读取的格式,这个过程叫序列化和反序列化。同理,从服务端返回的值也需要序列化和反序列化的过程。网络传输
远程调用往往用在网络上,客户端和服务端是通过网络连接的。所有的数据都需要通过网络传输,因此就需要有一个网络传输层。
网络传输层需要把Call ID和序列化后的参数字节流传给服务端,然后再把序列化后的调用结果传回客户端。只要能完成这两者的,
都可以作为传输层使用。因此,它所使用的协议其实是不限的,能完成传输就行。尽管大部分RPC框架都使用TCP协议,但其实UDP也可以,
而gERPC干脆就用了HTTP2。
HTTP、RPC和restful之间的区别
RPC和HTTP
RPC必须要依赖网络传输协议,HTTP本身属于网络协议的一种,网络协议HTTP可以传输,我们直接基于TCP协议直接链接也可以,
HTTP协议只是实现RPC框架的一种选择。
RPC和restful
RPC和restful不是非此即彼,一般我们的服务想要对外提供服务的时候一般采用的是HTTP请求,但是这么多接口按照什么规范放出去,
这就是restful。restful只是一个规范而已,你完全可以不遵守。RPC一般是系统内部之间服务调用,
其中RPC的协议灵活性会使得我们传输性能更高,但是因为使用不方便所以一般在系统内部组件之间调用的时候使用。