参考视频:https://www.imooc.com/learn/1158
参考博客:https://www.zhihu.com/question/25536695/answer/221638079
本地过程调用
RPC就是要像调用本地的函数一样去调远程函数。在研究RPC前,我们先看看本地调用是怎么调的。假设我们要调用函数Multipl
y来计算lvalue * rvalue的结果:
1 int Multiply(int l, int r) { 2 int y = l * r; 3 return y; 4 } 5 6 int lvalue = 10; 7 int rvalue = 20; 8 int l_times_r = Multiply(lvalue, rvalue);
那么在第8行时,我们实际上执行了以下操作:
以上5步就是执行本地调用的过程。(20190116注:以上步骤只是为了说明原理。事实上编译器经常会做优化,对于参数和返回值少的情况会直接将其存放在寄存器,而不需要压栈弹栈的过程,甚至都不需要调用call,而直接做inline操作。仅就原理来说,这5步是没有问题的。)
远程过程调用带来的新问题
在远程调用时,我们需要执行的函数体是在远程的机器上的,也就是说,Multiply是在另一个进程中执行的。这就带来了几个新问题:
有了这三个机制,就能实现RPC了,具体过程如下:
// Client端 // int l_times_r = Call(ServerAddr, Multiply, lvalue, rvalue) 1. 将这个调用映射为Call ID。这里假设用最简单的字符串当Call ID的方法 2. 将Call ID,lvalue和rvalue序列化。可以直接将它们的值以二进制形式打包 3. 把2中得到的数据包发送给ServerAddr,这需要使用网络传输层 4. 等待服务器返回结果 5. 如果服务器调用成功,那么就将结果反序列化,并赋给l_times_r // Server端 1. 在本地维护一个Call ID到函数指针的映射call_id_map,可以用std::map<std::string, std::function<>> 2. 等待请求 3. 得到一个请求后,将其数据包反序列化,得到Call ID 4. 通过在call_id_map中查找,得到相应的函数指针 5. 将lvalue和rvalue反序列化后,在本地调用Multiply函数,得到结果 6. 将结果序列化后通过网络返回给Client
所以要实现一个RPC框架,其实只需要按以上流程实现就基本完成了。
其中:
当然,这里面还有一些细节可以填充,比如如何处理网络错误,如何防止攻击,如何做流量控制,等等。但有了以上的架构,这些都可以持续加进去。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------