远程过程调用RPC
RPC发展的背景
软件架构发展背景
-
单体架构
在早期的软件架构中以单体架构为主,就是传统的MVC架构,一个项目的所有代码由一个应用搞定。
这种架构不能支持高并发与高稳定性,淘汰。
-
分布式架构
在单体架构上进行了改进,把一个项目按照模块或者功能拆分为多个项目,多个项目分别部署到不同的服务器上。
优点
- 减少单点故障,提高系统稳定性
- 增加重用性。
- 增加可扩展性。
- 增加负载能力。
缺点
- 成本高,架构复杂
- 整体响应之间边长,设计多个服务间通信
- I/O吞吐量变大
-
RPC协议出现
从单体架构到分布式架构的发展,不同服务部署在不同的主机上,随之也出现了服务间跨主机通信的问题,也就是跨主机间代码调用的问题。
为了解决该问题,互联网协会(Internet Society, IOSC)提出了RPC(Remote Procedure Call)协议(远程调用协议)。
RPC协议规定允许互联网中一台主机程序调用另一台主机程序,而程序员无需关心底层调用实现。
RPC是上层协议,底层基于TCP或者HTTP协议实现都可以。
RPC是基于分布式架构而出现的,所以RPC在分布式项目中占据优势。
RPC与HTTP对比
- 效率:RPC协议报文相较于HTTP1简单,体积小。
- 连接方式:RPC支持长连接,HTTP三次握手
- 性能:RPC根据使用语言的不同可以有很多不同种的序列化方式,HTTP主要通过JSON
- 注册中心:RPC框架一般都带有注册中心,HTTP都是直连
- 负载均衡:RPC框架一般都带有负载均衡策略,HTTP需要借助nginx等工具
RPC机制和实现过程
RPC机制
广义:RPC一般采用C/S模式,从广义上讲,所有客户端服务器交互方式都可以成为RPC。
**狭义:**RPC是指基于底层协议二进制流,并提供类似于本地方法调用形式的客户端服务器交互方式,使客户端与服务器都察觉不出是远程调用。
如何使客户端与服务器都察觉不出是远程调用
就像代理模式一样,此时就需要一个中间人来做代理,这样双方就都无法察觉是远程调用。
而这个代理人客户端与服务端双方都有:客户存根(client stub),服务存根(server stub)
-
以客户端远程调用服务端read方法为例,调用图如下:
- 客户端进程(调用程序)以正常的方式调用客户存根
- 客户存根生成一个消息,然后调用本地操作系统的网络通信模块,存根进入阻塞状态
- 客户端将网络消息发送给远程操作系统
- 远程操作系统将网络消息交给服务端存根
- 服务端存根将参数提取出来,而后调用服务端程序
- 服务端程序执行响应的操作,操作完成后将结果返回给服务端存根
- 服务端存根将结果打包成一个消息,而后调用本地操作系统
- 服务端操作系统将含有结果的消息发送给客户端操作系统
- 客户端操作系统将消息交给客户存根,存根从阻塞状态恢复,进入运行状态
- 客户存根将结果从消息中提取出来,返回给调用它的客户端过程
-
从上边的调用过程可以发现RPC,程序员调用远程服务时,无需关心网络通信等底层细节。
但实现一个RPC远程调用需要考虑以下问题:
- 参数传递
- 通信协议制定
- 出错和超时处理等
传递参数
- 客户端和服务器会将参数封装至消息体中,通过网络传输的方式发送至对方,然后双方的存根解析消息体,将参数抽取。
- RPC支持传递值参数,一般不支持传递引用参数,因为传递引用参数需要复制一个副本值目标服务器,然后参数引用该副本。
- 在分布式系统中,不同机器的数据格式不一定相同,所以在消息传递的时候,需要将消息使用统一编码格式编码:比如JSON,及各种XML格式
通信协议定制
-
协议栈分为公有协议和私有协议,公有协议:HTTP, SMPP, WebService 等。私有协议:google 的 Thrift 协议等
-
私有协议优点:可以最大程度地提升性能,降低成本,提高灵活性与效率
缺点:公网传输能力弱
私有协议的设计
协议栈的设计需要考虑以下方面:编码器,解码器,心跳,命令协议和命令处理器
-
协议设计
协议中的每个字段都要是必要的。
协议需要考虑通信功能特性的支持,比如CRC校验,安全校验,数据压缩机制等。
必须考虑协议的可扩展性。
-
私有协议的编解码
能够针对业务负载设计不同的编解码机制。
-
命令定义和命令处理器
负载命令(Payload Command): 指传输业务的具体数据,比如请求参数,响应结果的命令。
控制命令(Control Command):一般为功能管理命令,心跳命令等,通常完成复杂的分布式跨界点的协调功能,以此保证负载命令通信过程的稳定。
命令处理器:命令对应业务的处理逻辑。
-
命令协议
同一种协议可以支持多种序列化方式,不同协议在编解码斜率和传输效率上都有所不同
-
通信模式
四种通信模式:
Oneway
不关心响应,请求线程不会被阻塞,但使用时需要注意控制调用流量,进行蓄洪,防止压垮下游业务服务。
Sync
调用会阻塞请求线程,待响应返回后才能进行下一个请求。
Future
在调用过程不会阻塞线程,但获取结果的过程会阻塞线程。
Callback
异步调用,不会阻塞线程
出错和超时处理
-
远程请求服务端可能会出错,分为三种情况:
-
服务端未接收到客户端请求出错,执行过程被处理0次。
-
服务端接收到请求并返回响应,客户端在收到响应前,服务端出错。
如果客户端有重试机制,最终过程会被执行多次。
如果客户端没有重试机制,最终过程会被执行一次。
-
客户端请求超时,发起多次调用,远程过程会被执行多次。
-
-
因此,RPC系统通常会提供至少一次或最多一次的语义。