RPC系列:基本概念

RPC(Remote Procedure Call):远程过程调用,它是一种经过网络从远程计算机程序上请求服务,而不须要了解底层网络技术的思想。html

RPC 是一种技术思想而非一种规范或协议,常见 RPC 技术和框架有:java

  • 应用级的服务框架:阿里的 Dubbo/Dubbox、Google gRPC、Spring Boot/Spring Cloud、Facebook 的 Thrift、Twitter 的 Finagle 等。
  • 远程通讯协议:RMI、Socket、SOAP(HTTP XML)、REST(HTTP JSON)。
  • 通讯框架:MINA 和 Netty。
  • ps: Google gRPC 框架是基于 HTTP2 协议实现的,底层使用到了 Netty 框架的支持。

1. RPC 框架

一个典型 RPC 的使用场景中,包含了服务发现、负载、容错、网络传输、序列化等组件,其中“RPC 协议”就指明了程序如何进行网络传输和序列化。服务器

图 1:完整 RPC 架构图网络

2. RPC 核心功能

一个 RPC 的核心功能主要有 5 个部分组成,分别是:客户端、客户端 Stub、网络传输模块、服务端 Stub、服务端等。架构

图 4:RPC 核心功能图并发

下面分别介绍核心 RPC 框架的重要组成:框架

  1. 客户端(Client):服务调用方。
  2. 客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再经过网络传输发送给服务端。
  3. 服务端存根(Server Stub):接收客户端发送过来的请求消息并进行解包,而后再调用本地服务进行处理。
  4. 服务端(Server):服务的真正提供者。
  5. Network Service:底层传输,能够是 TCP 或 HTTP。

一次 RPC 调用流程以下:函数

  1. 服务消费者(Client 客户端)经过本地调用的方式调用服务。
  2. 客户端存根(Client Stub)接收到调用请求后负责将方法、入参等信息序列化(组装)成可以进行网络传输的消息体。
  3. 客户端存根(Client Stub)找到远程的服务地址,而且将消息经过网络发送给服务端。
  4. 服务端存根(Server Stub)收到消息后进行解码(反序列化操做)。
  5. 服务端存根(Server Stub)根据解码结果调用本地的服务进行相关处理
  6. 服务端(Server)本地服务业务处理。
  7. 处理结果返回给服务端存根(Server Stub)。
  8. 服务端存根(Server Stub)序列化结果。
  9. 服务端存根(Server Stub)将结果经过网络发送至消费方。
  10. 客户端存根(Client Stub)接收到消息,并进行解码(反序列化)。
  11. 服务消费方获得最终结果。

RPC的目标就是要2~10这些步骤都封装起来,让用户对这些细节透明。工具

3. RPC 核心功能实现技术点

  • 透明化远程服务调用:字节码生成,JDK动态代理
  • 编码与解码
  • 服务寻址:Call ID 映射,能够直接使用函数字符串,也可使用整数 ID。映射表通常就是一个哈希表。
  • 数据流的序列化和反序列化:能够本身写,也可使用 Protobuf 或者 FlatBuffers 之类的。
  • 网络传输:能够本身写 Socket,或者用 Asio,ZeroMQ,Netty 之类。在 RPC 中可选的网络传输方式有多种,能够选择 TCP 协议、UDP 协议、HTTP 协议

3.1  透明化远程服务调用(代理)

  1. jdk 动态代理:更多使用动态代理
  2. 字节码生成:更为强大和高效,但代码维护不易

3.2  服务寻址(服务注册中心)

  实现方式:服务注册中心。post

  Call ID 映射

/**
  1) 服务寻址可使用 Call ID 映射。在本地调用中,函数体是直接经过函数指针来指定的,可是在远程调用中,函数指针是不行的,由于两个进程的地址空间是彻底不同的。

  2) 因此在 RPC 中,全部的函数都必须有本身的一个 ID。这个 ID 在全部进程中都是惟一肯定的。

  3) 客户端在作远程过程调用时,必须附上这个 ID。而后咱们还须要在客户端和服务端分别维护一个函数和Call ID的对应表。

  4) 当客户端须要进行远程调用时,它就查一下这个表,找出相应的 Call ID,而后把它传给服务端,服务端也经过查表,来肯定客户端须要调用的函数,而后执行相应函数的代码。
**/

实现案例:RMI(Remote Method Invocation,远程方法调用)也就是 RPC 自己的实现方式。

图 9:RMI 架构图

Registry(服务发现):借助 JNDI 发布并调用了 RMI 服务。实际上,JNDI 就是一个注册表,服务端将服务对象放入到注册表中,客户端从注册表中获取服务对象。

3.3  编码与解码

客户端的请求消息结构通常须要包括如下内容:
/**
    1)接口名称:服务端就调用的那个接口;
    2)方法名:一个接口内可能有不少方法,若是不传方法名服务端也就不知道调用哪一个方法;
    3)参数类型&参数值:参数类型有不少,好比有bool、int、long、double、string、map、list,甚至如struct(class);以及相应的参数值;
    4)超时时间
    5)requestID,标识惟一请求id
*/

服务端返回的消息结构通常包括如下内容。
/**
    1)返回值
    2)状态code
    3)requestID 
*/

3.4  序列化与反序列化

客户端怎么把参数值传给远程的函数呢?在本地调用中,咱们只须要把参数压到栈里,而后让函数本身去栈里读就行。

可是在远程过程调用时,客户端跟服务端是不一样的进程,不能经过内存来传递参数。

这时候就须要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成本身能读取的格式。

只有二进制数据才能在网络中传输,序列化和反序列化的定义是:

  • 将对象转换成二进制流的过程叫作序列化
  • 将二进制流转换成对象的过程叫作反序列化

这个过程叫序列化和反序列化。同理,从服务端返回的值也须要序列化反序列化的过程。

目前互联网公司普遍使用Protobuf、Thrift、Avro等成熟的序列化解决方案来搭建RPC框架,这些都是久经考验的解决方案。

 

3.5 网络传输

网络传输:远程调用每每用在网络上,客户端和服务端是经过网络链接的。

全部的数据都须要经过网络传输,所以就须要有一个网络传输层。网络传输层须要把 Call ID 和序列化后的参数字节流传给服务端,而后再把序列化后的调用结果传回客户端。

网络协议
    1. 尽管大部分 RPC 框架都使用 TCP 协议,但其实 UDP 也能够,而 gRPC 干脆就用了 HTTP2。
    2. 在 RPC 中可选的网络传输方式有多种,能够选择 TCP 协议、UDP 协议、HTTP 协议

网络框架
  能够本身写 Socket,或者用 Asio,ZeroMQ,Netty 之类。

网络通讯
  目前有两种经常使用IO通讯模型:1)BIO;2)NIO。通常RPC框架须要支持这两种IO模型。
  如何实现RPC的IO通讯框架呢?
    1. 使用java nio方式自研,这种方式较为复杂,并且颇有可能出现隐藏bug,但也见过一些互联网公司使用这种方式;
    2. 基于mina,mina在早几年比较火热,不过这些年版本更新缓慢;
    3. 基于netty,如今不少RPC框架都直接基于netty这一IO通讯框架,省力又省心,好比阿里巴巴的HSF、dubbo,Twitter的finagle等。

 

基于 TCP 协议的 RPC 调用

/**
    由服务的调用方与服务的提供方创建 Socket 链接,并由服务的调用方经过 Socket 将须要调用的接口名称、方法名称和参数序列化后传递给服务的提供方,服务的提供方反序列化后再利用反射调用相关的方法。
    可是在实例应用中则会进行一系列的封装,如 RMI 即是在 TCP 协议上传递可序列化的 Java 对象。
*/

基于 HTTP 协议的 RPC 调用
/**
    该方法更像是访问网页同样,只是它的返回结果更加单一简单。

    其大体流程为:由服务的调用者向服务的提供者发送请求,这种请求的方式多是 GET、POST、PUT、DELETE 等中的一种,服务的提供者可能会根据不一样的请求方式作出不一样的处理,或者某个方法只容许某种请求方式。

    而调用的具体方法则是根据 URL 进行方法调用,而方法所须要的参数多是对服务调用方传输过去的 XML 数据或者 JSON 数据解析后的结果,返回 JOSN 或者 XML 的数据结果。

    因为目前有不少开源的 Web 服务器,如 Tomcat,因此其实现起来更加容易,就像作 Web 项目同样。
*/

两种方式对比
/**
    基于 TCP 的协议实现的 RPC 调用,因为 TCP 协议处于协议栈的下层,可以更加灵活地对协议字段进行定制,减小网络开销,提升性能,实现更大的吞吐量和并发数。

    可是须要更多关注底层复杂的细节,实现的代价更高。同时对不一样平台,如安卓,iOS 等,须要从新开发出不一样的工具包来进行请求发送和相应解析,工做量大,难以快速响应和知足用户需求。

    基于 HTTP 协议实现的 RPC 则可使用 JSON 和 XML 格式的请求或响应数据。

    而 JSON 和 XML 做为通用的格式标准(使用 HTTP 协议也须要序列化和反序列化,不过这不是该协议下关心的内容,成熟的 Web 程序已经作好了序列化内容),开源的解析工具已经至关成熟,在其上进行二次开发会很是便捷和简单。

    可是因为 HTTP 协议是上层协议,发送包含同等内容的信息,使用 HTTP 协议传输所占用的字节数会比使用 TCP 协议传输所占用的字节数更高。

    所以在同等网络下,经过 HTTP 协议传输相同内容,效率会比基于 TCP 协议的数据效率要低,信息传输所占用的时间也会更长,固然压缩数据,可以缩小这一差距。
*/

 

4. 摘录网址

  1. 花了一个星期,我终于把RPC框架整明白了!
  2. RPC原理及RPC实例分析
相关文章
相关标签/搜索