<微服务架构>—RPC入门篇

概念

RPC(Remote Procedure Call):远程过程调用,它是一种经过网络从远程计算机程序上请求服务,而不须要了解底层网络技术的思想.RPC 是一种技术思想而非一种规范或协议,常见 RPC 技术和框架有:web

  • 应用级的服务框架:阿里的 Dubbo/Dubbox、Google gRPC、Spring Boot/Spring Cloud
  • 远程通讯协议:RMI、Socket、SOAP(HTTP XML)、REST(HTTP JSON)
  • 通讯框架:MINA 和 Netty

目前流行的开源 RPC 框架仍是比较多的,有阿里巴巴的 Dubbo、Facebook 的 Thrift、Google 的 gRPC、Twitter 的 Finagle 等。编程

  • gRPC:是 Google 公布的开源软件,基于HTTP 2.0的协议,并支持常见的众多编程语言。RPC 框架是基于 HTTP 协议实现的,底层使用到了 Netty 框架的支持。
  • Thrift:是 Facebook 的开源 RPC 框架,主要是一个跨语言的服务开发框架。
    用户只要在其之上进行二次开发就行,应用对于底层的 RPC 通信等都是透明的。不过这个对于用户来讲须要学习特定领域语言这个特性,仍是有必定成本的。json

  • Dubbo:是阿里集团开源的一个极为出名的 RPC 框架,在不少互联网公司和企业应用中普遍使用。协议和序列化框架均可以插拔是极其鲜明的特点。

完整的RPC框架

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

<微服务架构>—RPC入门篇

RPC核心功能

RPC 的核心功能是指实现一个 RPC 最重要的功能模块,就是上图中的”RPC 协议”部分:安全

<微服务架构>—RPC入门篇

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

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

一次 RPC 调用流程以下:restful

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

RPC核心之功能实现

接口调用一般包含两个部分,序列化和通讯协议。常见的序列化协议包括json、xml、hession、protobuf、thrift、text、bytes等;通讯比较流行的是http、soap、websockect,RPC一般基于TCP实现。那么restful使用的序列化协议一般是json,通讯协议是http;rpc是一种通讯协议,所以若是序列化使用json的话,那么就是json-rpc网络

RPC 的核心功能主要由 5 个模块组成,若是想要本身实现一个 RPC,最简单的方式要实现三个技术点,分别是:架构

  • 服务寻址
  • 数据流的序列化和反序列化
  • 网络传输

服务寻址

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

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

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

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

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

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

RMI 服务在服务端实现以后须要注册到 RMI Server 上,而后客户端从指定的 RMI 地址上 Lookup 服务,调用该服务对应的方法便可完成远程方法调用。

Registry 是个很重要的功能,当服务端开发完服务以后,要对外暴露,若是没有服务注册,则客户端是无从调用的,即便服务端的服务就在那里

序列化和反序列化

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

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

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

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

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

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

网络传输

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

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

只要能完成这二者的,均可以做为传输层使用。所以,它所使用的协议实际上是不限的,能完成传输就行。

尽管大部分 RPC 框架都使用 TCP 协议,但其实 UDP 也能够,而 gRPC 干脆就用了 HTTP2。

TCP 的链接是最多见的,简要分析基于 TCP 的链接:一般 TCP 链接能够是按需链接(须要调用的时候就先创建链接,调用结束后就立马断掉),也能够是长链接(客户端和服务器创建起链接以后保持长期持有,无论此时有无数据包的发送,能够配合心跳检测机制按期检测创建的链接是否存活有效),多个远程过程调用共享同一个链接。

因此,要实现一个 RPC 框架,只须要把如下三点实现了就基本完成了:

  • Call ID 映射:能够直接使用函数字符串,也可使用整数 ID。映射表通常就是一个哈希表。
  • 序列化反序列化:能够本身写,也可使用 Protobuf 或者 FlatBuffers 之类的。
  • 网络传输库:能够本身写 Socket,或者用 Asio,ZeroMQ,Netty 之类。

RPC 核心之网络传输协议

在 RPC 中可选的网络传输方式有多种,能够选择 TCP 协议、UDP 协议、HTTP 协议。

基于 TCP 协议的 RPC 调用

由服务的调用方与服务的提供方创建 Socket 链接,并由服务的调用方经过 Socket 将须要调用的接口名称、方法名称和参数序列化后传递给服务的提供方,服务的提供方反序列化后再利用反射调用相关的方法。

将结果返回给服务的调用方,整个基于 TCP 协议的 RPC 调用大体如此。

可是在实例应用中则会进行一系列的封装,如 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 协议的数据效率要低,信息传输所占用的时间也会更长,固然压缩数据,可以缩小这一差距。

使用 RabbitMQ 的 RPC 架构

在 OpenStack 中服务与服务之间使用 RESTful API 调用,而在服务内部则使用 RPC 调用各个功能模块。

正是因为使用了 RPC 来解耦服务内部功能模块,使得 OpenStack 的服务拥有扩展性强,耦合性低等优势。

OpenStack 的 RPC 架构中,加入了消息队列 RabbitMQ,这样作的目的是为了保证 RPC 在消息传递过程当中的安全性和稳定性。

下面分析 OpenStack 中使用 RabbitMQ 如何实现 RPC 的调用。

使用 RabbitMQ 的好处:

  • 同步变异步:可使用线程池将同步变成异步,可是缺点是要本身实现线程池,而且强耦合。使用消息队列能够轻松将同步请求变成异步请求。
  • 低内聚高耦合:解耦,减小强依赖。
  • 流量削峰:经过消息队列设置请求值,超过阀值的抛弃或者转到错误界面。
  • 网络通讯性能提升:TCP 的建立和销毁开销大,建立 3 次握手,销毁 4 次分手,高峰时成千上万条的连接会形成资源的巨大浪费,并且操做系统每秒处理 TCP 的数量也是有数量限制的,一定形成性能瓶颈。
  • RabbitMQ 采用信道通讯,不采用 TCP 直接通讯。一条线程一条信道,多条线程多条信道,公用一个 TCP 链接。

简单对比 RPC 和 Restful API

REST API的几个特色为:

  • 资源:就是网络上的一个实体,或者说是网络上的一个具体信息。它能够是一段文本、一张图片、一首歌曲、一种服务,就是一个具体的实在
  • 统一接口:RESTful 架构风格规定,数据的元操做,即 CRUD(Create,Read,Update 和 Delete,即数据的增删查改)操做,分别对应于 HTTP 方法:GET 用来获取资源,POST 用来新建资源(也能够用于更新资源),PUT 用来更新资源,DELETE 用来删除资源,这样就统一了数据操做的接口,仅经过 HTTP 方法,就能够完成对数据的全部增删查改工做。
  • URL:能够用一个 URI(统一资源定位符)指向资源,即每一个 URI 都对应一个特定的资源。
    要获取这个资源,访问它的 URI 就能够,所以 URI 就成了每个资源的地址或识别符
  • 无状态:所谓无状态的,即全部的资源,均可以经过 URI 定位,并且这个定位与其余资源无关,也不会由于其余资源的变化而改变

RPC 和 Restful API 对比

  • 面对对象不一样:RPC 更侧重于动做,REST 的主体是资源
  • 传输效率:RPC 效率更高。RPC,使用自定义的 TCP 协议,可让请求报文体积更小,或者使用 HTTP2 协议,也能够很好的减小报文的体积,提升传输效率
  • 复杂度:RPC 实现复杂,流程繁琐,REST 调用及测试都很方便
  • 灵活性:HTTP 相对更规范,更标准,更通用,不管哪一种语言都支持 HTTP 协议;RPC 能够实现跨语言调用,但总体灵活性不如 RESTful

总结

RPC 主要用于公司内部的服务调用,性能消耗低,传输效率高,实现复杂

RESTful API 主要用于对外的异构环境,浏览器接口调用,App 接口调用,第三方接口调用等

RPC 使用场景(大型的网站,内部子系统较多、接口很是多的状况下适合使用 RPC):

  • 长连接。没必要每次通讯都要像 HTTP 同样去 3 次握手,减小了网络开销。
  • 注册发布机制。RPC 框架通常都有注册中心,有丰富的监控管理;发布、下线接口、动态扩展等,对调用方来讲是无感知、统一化的操做。
  • 安全性,没有暴露资源操做。
  • 微服务支持。就是最近流行的服务化架构、服务化治理,RPC 框架是一个强力的支撑。
相关文章
相关标签/搜索