本文转自https://developer.51cto.com/a...html
RPC(Remote Procedure Call):远程过程调用,它是一种经过网络从远程计算机程序上请求服务,而不须要了解底层网络技术的思想。git
RPC 是一种技术思想而非一种规范或协议,常见 RPC 技术和框架有:程序员
目前流行的开源 RPC 框架仍是比较多的,有阿里巴巴的 Dubbo、Facebook 的 Thrift、Google 的 gRPC、Twitter 的 Finagle 等。github
在一个典型 RPC 的使用场景中,包含了服务发现、负载、容错、网络传输、序列化等组件,其中“RPC 协议”就指明了程序如何进行网络传输和序列化。面试
图 1:完整 RPC 架构图编程
以下是 Dubbo 的设计架构图,分层清晰,功能复杂:小程序
图 2:Dubbo 架构图浏览器
RPC 核心功能安全
RPC 的核心功能是指实现一个 RPC 最重要的功能模块,就是上图中的”RPC 协议”部分:服务器
图 3:RPC 核心功能
一个 RPC 的核心功能主要有 5 个部分组成,分别是:客户端、客户端 Stub、网络传输模块、服务端 Stub、服务端等。
图 4:RPC 核心功能图
下面分别介绍核心 RPC 框架的重要组成:
Server.py:
fromSimpleXMLRPCServer importSimpleXMLRPCServer deffun_add(a,b): totle = a + b returntotle if__name__ == '__main__': s = SimpleXMLRPCServer(( '0.0.0.0', 8080)) #开启xmlrpcserver s.register_function(fun_add) #注册函数fun_add print"server is online..." s.serve_forever #开启循环等待
Client.py:
fromxmlrpclib importServerProxy #导入xmlrpclib的包 s = ServerProxy( "http://172.171.5.205:8080") #定义xmlrpc客户端 prints.fun_add( 2, 3) #调用服务器端的函数
开启服务端:
开启客户端:
Wireshark 抓包分析过程
客户端去往服务端:
通讯使用 HTTP 协议,XML 文件传输格式。传输的字段包括:方法名 methodName,两个参数 2,3。
图 5:Request 抓包
服务端返回结果,字段返回值 Value,结果是 5:
图 6:Response 抓包
在这两次网络传输中使用了 HTTP 协议,创建 HTTP 协议之间有 TCP 三次握手,断开 HTTP 协议时有 TCP 四次挥手。
图 7:基于 HTTP 协议的 RPC 链接过程
详细调用过程
Python 自带 RPC 的 Demo 小程序的实现过程,流程和分工角色能够用下图来表示:
图 8:RPC 调用详细流程图
一次 RPC 调用流程以下:
RPC 的核心功能主要由 5 个模块组成,若是想要本身实现一个 RPC,最简单的方式要实现三个技术点,分别是:
服务寻址可使用 Call ID 映射。在本地调用中,函数体是直接经过函数指针来指定的,可是在远程调用中,函数指针是不行的,由于两个进程的地址空间是彻底不同的。
因此在 RPC 中,全部的函数都必须有本身的一个 ID。这个 ID 在全部进程中都是惟一肯定的。
客户端在作远程过程调用时,必须附上这个 ID。而后咱们还须要在客户端和服务端分别维护一个函数和Call ID的对应表。
当客户端须要进行远程调用时,它就查一下这个表,找出相应的 Call ID,而后把它传给服务端,服务端也经过查表,来肯定客户端须要调用的函数,而后执行相应函数的代码。
实现方式 服务注册中心。
要调用服务,首先你须要一个服务注册中心去查询对方服务都有哪些实例。Dubbo 的服务注册中心是能够配置的,官方推荐使用 Zookeeper。
实现案例:
RMI(Remote Method Invocation,远程方法调用)也就是 RPC 自己的实现方式。
图 9:RMI 架构图
Registry(服务发现):
借助 JNDI 发布并调用了 RMI服务。实际上,JNDI 就是一个注册表,服务端将服务对象放入到注册表中,客户端从注册表中获取服务对象。
RMI 服务在服务端实现以后须要注册到 RMI Server 上,而后客户端从指定的 RMI 地址上 Lookup 服务,调用该服务对应的方法便可完成远程方法调用。
Registry 是个很重要的功能,当服务端开发完服务以后,要对外暴露,若是没有服务注册,则客户端是无从调用的,即便服务端的服务就在那里。
客户端怎么把参数值传给远程的函数呢?在本地调用中,咱们只须要把参数压到栈里,而后让函数本身去栈里读就行。
可是在远程过程调用时,客户端跟服务端是不一样的进程,不能经过内存来传递参数。
这时候就须要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成本身能读取的格式。
这个过程叫序列化和反序列化。同理,从服务端返回的值也须要序列化反序列化的过程。
优势
1 简单易用开发成本低
2 跨语言
3 轻量级数据交换
4 非冗长性(对比xml标签简单括号闭环)
缺点
1 体积大,影响高并发
2 无版本检查,本身作兼容
3 片断的建立和验证过程比通常的XML复杂
4 缺少命名空间致使信息混合
总结:最简单最通用的应用协议,使用普遍,开发效率高,性能相对较低,维护成本较高。
Protobuf是一种以有效并可扩展的格式编码结构化数据的方式。
优势
1 跨语言,可自定义数据结构。
2 字段被编号,新添加的字段不影响老结构。解决了向后兼容问题。
3 自动化生成代码,简单易用。
4 二进制消息,效率高,性能高。
5 Netty等框架集成了该协议,提供了编×××提升开发效率。
缺点
1 二进制格式,可读性差(抓包dump后的数据很难看懂)
2 对象冗余,字段不少,生成的类较大,占用空间。
3 默认不具有动态特性(能够经过动态定义生成消息类型或者动态编译支持)
总结:简单快速上手,高效兼容性强,维护成本较高。
优势
1 序列化和RPC支持一站式解决,比pb更方便
2 跨语言,IDL接口定义语言,自动生成多语言文件
3 省流量,体积较小
4 包含完整的客户端/服务端堆栈,可快速实现RPC
5 为服务端提供了多种工做模式,如线程池模型、非阻塞模型
缺点
1 早期版本问题较大,0.7之前有兼容性问题
2 不支持双通道
3 rpc方法非线程安全,服务器容易被挂死,须要串行化。
4 默认不具有动态特性(能够经过动态定义生成消息类型或者动态编译支持)
5 开发环境、编译较麻烦
总结:跨语言、实现简单,初次使用较麻烦,须要避免使用问题和场景限制。
网络传输:
远程调用每每用在网络上,客户端和服务端是经过网络链接的。
全部的数据都须要经过网络传输,所以就须要有一个网络传输层。网络传输层须要把 Call ID 和序列化后的参数字节流传给服务端,而后再把序列化后的调用结果传回客户端。
只要能完成这二者的,均可以做为传输层使用。所以,它所使用的协议实际上是不限的,能完成传输就行。
尽管大部分 RPC 框架都使用 TCP 协议,但其实 UDP 也能够,而 gRPC 干脆就用了 HTTP2。
TCP 的链接是最多见的,简要分析基于 TCP 的链接:
一般 TCP 链接能够是按需链接(须要调用的时候就先创建链接,调用结束后就立马断掉),也能够是长链接(客户端和服务器创建起链接以后保持长期持有,无论此时有无数据包的发送,能够配合心跳检测机制按期检测创建的链接是否存活有效),多个远程过程调用共享同一个链接。
因此,要实现一个 RPC 框架,只须要把如下三点实现了就基本完成了:
RPC 核心之网络传输协议
在第三节中说明了要实现一个 RPC,须要选择网络传输的方式。
图 10:网络传输
在 RPC 中可选的网络传输方式有多种,能够选择 TCP 协议、UDP 协议、HTTP 协议。
每一种协议对总体的性能和效率都有不一样的影响,如何选择一个正确的网络传输协议呢?首先要搞明白各类传输协议在 RPC 中的工做方式。
由服务的调用方与服务的提供方创建 Socket 链接,并由服务的调用方经过 Socket 将须要调用的接口名称、方法名称和参数序列化后传递给服务的提供方,服务的提供方反序列化后再利用反射调用相关的方法。
最后将结果返回给服务的调用方,整个基于 TCP 协议的 RPC 调用大体如此。
可是在实例应用中则会进行一系列的封装,如 RMI 即是在 TCP 协议上传递可序列化的 Java 对象。
该方法更像是访问网页同样,只是它的返回结果更加单一简单。
其大体流程为:
由服务的调用者向服务的提供者发送请求,这种请求的方式多是 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 协议的数据效率要低,信息传输所占用的时间也会更长,固然压缩数据,可以缩小这一差距。
在 OpenStack 中服务与服务之间使用 RESTful API 调用,而在服务内部则使用 RPC 调用各个功能模块。
正是因为使用了 RPC 来解耦服务内部功能模块,使得 OpenStack 的服务拥有扩展性强,耦合性低等优势。
OpenStack 的 RPC 架构中,加入了消息队列 RabbitMQ,这样作的目的是为了保证 RPC 在消息传递过程当中的安全性和稳定性。
下面分析 OpenStack 中使用 RabbitMQ 如何实现 RPC 的调用。
RabbitMQ 简介
如下摘录自知乎:
对于初学者,举一个饭店的例子来解释这三个分别是什么吧。不是百分百恰当,可是应该足以解释这三者的区别。
RPC:
假设你是一个饭店里的服务员,顾客向你点菜,可是你不会作菜,因此你采集了顾客要点什么以后告诉后厨去作顾客点的菜,这叫 RPC(remote procedure call),由于厨房的厨师相对于服务员而言是另一我的(在计算机的世界里就是 Remote 的机器上的一个进程)。厨师作好了的菜就是RPC的返回值。
任务队列和消息队列:
本质都是队列,因此就只举一个任务队列的例子。假设这个饭店在高峰期顾客不少,而厨师只有不多的几个,因此服务员们不得不把单子按下单顺序放在厨房的桌子上,供厨师们一个一个作,这一堆单子就是任务队列,厨师们每作完一个菜,就从桌子上的订单里再取出一个单子继续作菜。
角色分担以下图:
图 11:RabbitMQ 在 RPC 中角色
使用 RabbitMQ 的好处:
RabbitMQ 的三种类型的交换器
RabbitMQ 使用 Exchange(交换机)和 Queue(队列)来实现消息队列。
在 RabbitMQ 中一共有三种交换机类型,每一种交换机类型都有很鲜明的特征。
基于这三种交换机类型,OpenStack 完成两种 RPC 的调用方式。首先简单介绍三种交换机。
图 12:RabbitMQ 架构图
①广播式交换器类型(Fanout)
该类交换器不分析所接收到消息中的 Routing Key,默认将消息转发到全部与该交换器绑定的队列中去。
图 13:广播式交换机
②直接式交换器类型(Direct)
该类交换器须要精确匹配 Routing Key 与 Binding Key,如消息的 Routing Key = Cloud,那么该条消息只能被转发至 Binding Key = Cloud 的消息队列中去。
图 14:直接式交换机
③主题式交换器(Topic Exchange)
该类交换器经过消息的 Routing Key 与 Binding Key 的模式匹配,将消息转发至全部符合绑定规则的队列中。
Binding Key 支持通配符,其中“*”匹配一个词组,“#”匹配多个词组(包括零个)。
图 15:主题式交换机
注:以上四张图片来自博客园,若有侵权,请联系做者:https://www.cnblogs.com/dwlsx...。
当生产者发送消息 Routing Key=F.C.E 的时候,这时候只知足 Queue1,因此会被路由到 Queue 中。
若是 Routing Key=A.C.E 这时候会被同时路由到 Queue1 和 Queue2 中,若是 Routing Key=A.F.B 时,这里只会发送一条消息到 Queue2 中。
Nova 基于 RabbitMQ 实现两种 RPC 调用:
其中 RPC.CALL 基于请求与响应方式,RPC.CAST 只是提供单向请求,两种 RPC 调用方式在 Nova 中均有典型的应用场景。
RPC.CALL
RPC.CALL 是一种双向通讯流程,即 RabbitMQ 接收消息生产者生成的系统请求消息,消息消费者通过处理以后将系统相应结果反馈给调用程序。
图 16:RPC.CALL 原理图
一个用户经过 Dashboard 建立一个虚拟机,界面通过消息封装后发送给 NOVA-API。
NOVA-API 做为消息生产者,将该消息以 RPC.CALL 方式经过 Topic 交换器转发至消息队列。
此时,Nova-Compute 做为消息消费者,接收该信息并经过底层虚拟化软件执行相应虚拟机的启动进程。
待用户虚拟机成功启动以后,Nova-Compute 做为消息生产者经过 Direct 交换器和响应的消息队列将虚拟机启动成功响应消息反馈给 Nova-API。
此时 Nova-API 做为消息消费者接收该消息并通知用户虚拟机启动成功。
RPC.CALL 工做原理以下图:
图 17:RPC.CALL 具体实现图
工做流程:
若是有多个线程同时进行远程方法调用,这时创建在 Client Server 之间的 Socket 链接上会有不少双方发送的消息传递,先后顺序也多是随机的。
Server 处理完结果后,将结果消息发送给 Client,Client 收到不少消息,怎么知道哪一个消息结果是原先哪一个线程调用的?
Client 线程每次经过 Socket 调用一次远程接口前,生成一个惟一的 ID,即 Request ID(Request ID必需保证在一个 Socket 链接里面是惟一的),通常经常使用 AtomicLong 从 0 开始累计数字生成惟一 ID。
RPC.CAST
RPC.CAST 的远程调用流程与 RPC.CALL 相似,只是缺乏了系统消息响应流程。
一个 Topic 消息生产者发送系统请求消息到 Topic 交换器,Topic 交换器根据消息的 Routing Key 将消息转发至共享消息队列。
与共享消息队列相连的全部 Topic 消费者接收该系统请求消息,并把它传递给响应的服务端进行处理。
其调用流程如图所示:
图 18:RPC.CAST 原理图
链接设计
RabbitMQ 实现的 RPC 对网络的通常设计思路:消费者是长链接,发送者是短链接。但能够自由控制长链接和短链接。
通常消费者是长链接,随时准备接收处理消息;并且涉及到 RabbitMQ Queues、Exchange 的 auto-deleted 等没特殊需求不必作短链接。发送者可使用短链接,不会长期占住端口号,节省端口资源。
Nova 中 RPC 代码设计:
简单对比 RPC 和 Restful API
REST 最大的几个特色为:资源、统一接口、URI 和无状态。
①资源
所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它能够是一段文本、一张图片、一首歌曲、一种服务,就是一个具体的实在。
②统一接口
RESTful 架构风格规定,数据的元操做,即 CRUD(Create,Read,Update 和 Delete,即数据的增删查改)操做,分别对应于 HTTP 方法:GET 用来获取资源,POST 用来新建资源(也能够用于更新资源),PUT 用来更新资源,DELETE 用来删除资源,这样就统一了数据操做的接口,仅经过 HTTP 方法,就能够完成对数据的全部增删查改工做。
③URL
能够用一个 URI(统一资源定位符)指向资源,即每一个 URI 都对应一个特定的资源。
要获取这个资源,访问它的 URI 就能够,所以 URI 就成了每个资源的地址或识别符。
④无状态
所谓无状态的,即全部的资源,均可以经过 URI 定位,并且这个定位与其余资源无关,也不会由于其余资源的变化而改变。有状态和无状态的区别,举个简单的例子说明一下。
如查询员工的工资,若是查询工资是须要登陆系统,进入查询工资的页面,执行相关操做后,获取工资的多少,则这种状况是有状态的。
由于查询工资的每一步操做都依赖于前一步操做,只要前置操做不成功,后续操做就没法执行。
若是输入一个 URI便可获得指定员工的工资,则这种状况是无状态的,由于获取工资不依赖于其余资源或状态。
且这种状况下,员工工资是一个资源,由一个 URI与之对应,能够经过 HTTP 中的 GET 方法获得资源,这是典型的 RESTful 风格。
面对对象不一样:
RESTful 是面向资源的设计架构,但在系统中有不少对象不能抽象成资源,好比登陆,修改密码等而 RPC 能够经过动做去操做资源。因此在操做的全面性上 RPC 大于 RESTful。
传输效率:
复杂度:
RPC 实现(参见第一节)须要实现编码,序列化,网络传输等。而 RESTful 不要关注这些,RESTful 实现更简单。
灵活性:
RPC 主要用于公司内部的服务调用,性能消耗低,传输效率高,实现复杂。
HTTP 主要用于对外的异构环境,浏览器接口调用,App 接口调用,第三方接口调用等。
RPC 使用场景(大型的网站,内部子系统较多、接口很是多的状况下适合使用 RPC):
https://developer.51cto.com/a...
http://www.mamicode.com/info-...
Java技术仓库《Java程序员复习指南》
https://github.com/h2pl/Java-...
整合全网优质Java学习内容,帮助你从基础到进阶系统化复习Java
全网最热的Java面试指南,共200多页,很是实用,无论是用于复习仍是准备面试都是不错的。在公众号【Java技术江湖】回复“PDF”便可免费领取。