1. RPC特征
RPC:remote procedure call 远程过程调用
即两台服务器A,B,一个应用部署在A服务器上,另一个应用部署在B服务器上,A服务想要调用B服务提供的方法,则
-
call id映射
:我们怎么告诉远程机器我们要调用哪个方法,而不是其他方法?在本地调用中,函数体直接通过函数指针指定,我们调用queryById(int id)方法,编译器自动帮我们调用它对应的函数指针。但是在远程调用中,函数指针不行的,因为两个进程地址空间完全不一样,所以,在RPC中,所有的函数都必须有自己的一个id,这个id在所有进程中都是唯一确定的。客户端在做远程调用时,必须附上该id。我们还需要在A服务和B服务分别维护一个(函数和call id)的映射表,两者的表不一定要完全相同,但是相同的函数对应的call id必须相同。当A服务即客户端需要远程调用时,它就查一下该表,找出对应的call id,然后把它传给B服务即服务端,服务端也通过查表,来确定客户端需要调用的函数,然后执行函数代码 -
序列化和反序列化
:A服务需要把本地参数传给B服务远程函数,本地调用的过程中,我们只需要把参数压到栈里,然后让函数自己去栈里读就行。但是在远程调用过程中,A服务跟B服务是不同的进程,不是一个内存空间,不能通过内存来传递参数,因此需要A服务将参数转化为字节流传给B服务,然后B服务将字节流转化为自身能读取的格式,是一个序列化和反序列化的过程
序列化可以简单理解为对象 –> 字节的过程,同理,反序列化则是相反的过程。这一过程的目的可以理解为转义,然后方便传输。常见的序列化方式有 JDK 自带序列化(类实现Serializable 接口),Hessian 序列化,Kryo 序列化Protobuf 序列化等 -
网络传输
:数据准备好以后,传输通过:网络传输层需要把调用的ID和序列化后的参数传给服务端,然后把计算好的结果序列化传给客户端,因此TCP层即可完成上述过程,gRPC中采用的是HTTP2协议。
2. RPC具体过程
RPC的设计由Client,Client stub,Network ,Server stub,Server构成。 其中Client就是用来调用服务的,Cient stub是用来把调用的方法和参数序列化的(因为要在网络中传输,必须要把对象转变成字节),Network用来传输这些信息到Server stub, Server stub用来把这些信息反序列化的,Server就是服务的提供者,最终调用的就是Server提供的方法
- Client像调用本地服务似的调用远程服务;
- Client stub接收到调用后,将方法、参数序列化
- 客户端通过sockets将消息发送到服务端
- Server stub 收到消息后进行解码(将消息对象反序列化)
- Server stub 根据解码结果调用本地的服务
- 本地服务执行(对于服务端来说是本地执行)并将结果返回给Server stub
- Server stub将返回结果打包成消息(将结果消息对象序列化)
- 服务端通过sockets将消息发送到客户端
- Client stub接收到结果消息,并进行解码(将结果消息反序列化)
客户端得到最终结果
// Client端
// int l_times_r = Call(ServerAddr, Multiply, lvalue, rvalue)
- 将这个调用映射为Call ID。这里假设用最简单的字符串当Call ID的方法
- 将Call ID,lvalue和rvalue序列化。可以直接将它们的值以二进制形式打包
- 把2中得到的数据包发送给ServerAddr,这需要使用网络传输层
- 等待服务器返回结果
- 如果服务器调用成功,那么就将结果反序列化,并赋给l_times_r
// Server端
- 在本地维护一个Call ID到函数指针的映射call_id_map,可以用std::map<std::string, std::function<>>
- 等待请求
- 得到一个请求后,将其数据包反序列化,得到Call ID
- 通过在call_id_map中查找,得到相应的函数指针
- 将lvalue和rvalue反序列化后,在本地调用Multiply函数,得到结果
- 将结果序列化后通过网络返回给Client
3. RPC代码示例
https://gitee.com/ctfen_z/rpc
这里客户端只需要知道服务端的接口userService即可,通过动态代理隐藏了实现细节,服务端执行的时候,会根据注册中心找到具体实现类userServiceImpl,然后调用方法返回结果
4. 常用RPC框架
4.1 gRPC
gRPC是google开源的一个高性能、跨语言的rpc框架,基于最新的HTTP2.0协议(HTTP2.0是基于二进制的HTTP协议升级版本),基于protobuf3.x,还使用到了Netty框架的支持
客户端和服务端使用HTTP/2以Protocol Buffer格式交换二进制消息
流程:
- 需要使用protobuf定义接口,即.proto文件
- 使用compile工具生成特定语言的执行代码,比如java c/c++、python等,为了解决跨语言问题
- 启动一个server端,server端通过监听指定端口,来等待client连接请求,通常使用netty来构建,grpc内置了netty的支持
- 启动一个或多个client端,client也是基于netty,client通过与server建立TCP长连接,并发送请求,Request与Response均被封装成HTTP2的stream Frame,通过Netty Channel进行交互
4.2 Dubbo
Dubbo是阿里开源的一个高性能优秀的Java RPC框架,可以和Spring无缝集成,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现
5. RPC和HTTP
这里的RPC服务一般是指基于TCP/IP协议上开发的二进制协议服务
Http服务一般是指基于Http1.0 / Http1.1协议上开发的REST服务(注意:不包含HTTP2.0)
- 序列化方式:
RPC服务序列化是针对二进制协议(0/1)来做序列化和反序列化,所以性能高
而Http服务是基于文本的序列化和反序列化,需要读一行一行的文本(比如json格式的),进行序列化和反序列化,所以性能低。 - 报文长度:
RPC服务是自定义的传输协议,传输的报文都是干货
而Http服务里面包括很多没用的报文内容,比如Http Header里面的accept,referer等等 - 连接的复用:
RPC服务是基于TCP/IP协议的,是长连接
而Http服务大都是短连接,虽然Http1.1支持长连接,但是这个也是要取决于服务端是否支持长连接,不太可控
http 和 rpc 并不是一个并行概念
rpc是远端过程调用,其调用协议通常包含传输协议和序列化协议
传输协议包含: 如著名的 gRPC使用的 http2.0 协议,也有如dubbo一类的自定义报文的tcp协议
序列化协议包含: 如基于文本编码的 xml json,也有二进制编码的 protobuf hessian等
常用的http客户端
谁能用通俗的语言解释一下什么是 RPC 框架? - 慕课网的回答 - 知乎
https://www.zhihu.com/question/25536695/answer/2283227217
https://www.jianshu.com/p/7ac2b521e41b
网友评论