介绍
RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务。
本地过程调用
def add(a, b):
total = a +b
return total
total = add(1, 2)
print(total)
函数调用过程:
- 将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的协议灵活性会使得我们传输性能更高,但是因为使用不方便所以一般在系统内部组件之间调用的时候使用。