RPC简单学习,Demo。
什么是RPC
RPC,Remote Procedure Call,远程过程调用。服务器告诉远程机器调用函数体。也有称藕式调用。
本地过程调用
进程内部的调用,比如main函数调用fun函数。
1 | public int add(int a, int b){ |
远程过程调用
进程间的通信(同机或者异机)可以理解为远程调用。从字面意思来理解,远程调用就是客户端(调用的模块)和服务端(被调用的模块)“不在一起”,“相隔很远”;本地调用就是客户端(调用的模块)和服务端(被调用的模块)“在一起”,“相隔很近”。实质就是,客户端与服务端的EJB对象不在同一个JVM进程中,就是远程调用;客户端与服务端的EJB对象在同一个JVM进程中,就是本地调用。
问题
- Call ID映射:在本地调用中,函数体是直接通过函数指针来指定的,比如调用add(),编译器会自动调用它相应的函数指针。但是在远程调用中,函数指针显然是不行的,因为两个进程的地址空间是完全不一样的。所以在RPC中,所有的函数都必须有一个自己的ID。这个ID在所有的进程中都是唯一确定的。客户端在做远程调用时,必须附上这个ID。此外还要再客户端和服务端分别维护
函数-CallID
的表。两者的表不一定要完全相同,但相同的函数对应的CallID必须相同。当客户端需要进行远程调用时,他就查一下这个表,找出相应的CallID,然后把它传给服务端,服务端也通过查表,来确定用户调用的函数,然后执行相应函数的代码。 - 序列化和反序列化:在本地调用中,我们只需要把参数压到栈里,然后让函数自己去栈里读就行。但是在远程过程调用时,客户端跟服务端是不同的进程,不能通过内存来传递参数。甚至有时候客户端和服务端使用的都不是同一种语言(比如服务端用C++,客户端用Java或者Python)。这时候就需要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成自己能读取的格式。这个过程叫序列化和反序列化。同理,从服务端返回的值也需要序列化反序列化的过程。
- 网络传输。远程调用往往用在网络上,客户端和服务端是通过网络连接的。所有的数据都需要通过网络传输,因此就需要有一个网络传输层。网络传输层需要把Call ID和序列化后的参数字节流传给服务端,然后再把序列化后的调用结果传回客户端。只要能完成这两者的,都可以作为传输层使用。因此,它所使用的协议其实是不限的,能完成传输就行。尽管大部分RPC框架都使用TCP协议,但其实UDP也可以,而gRPC干脆就用了HTTP2。
怎么RPC
1 | // Client端 |
实现RPC
// 待补录