RPC(Remote Procedure Call):远程过程调用,它是一种经过网络从远程计算机程序上请求服务,而不须要了解底层网络技术的思想。html
RPC 是一种技术思想而非一种规范或协议,常见 RPC 技术和框架有:java
一个典型 RPC 的使用场景中,包含了服务发现、负载、容错、网络传输、序列化等组件,其中“RPC 协议”就指明了程序如何进行网络传输和序列化。服务器
图 1:完整 RPC 架构图网络
一个 RPC 的核心功能主要有 5 个部分组成,分别是:客户端、客户端 Stub、网络传输模块、服务端 Stub、服务端等。架构
图 4:RPC 核心功能图并发
下面分别介绍核心 RPC 框架的重要组成:框架
一次 RPC 调用流程以下:函数
RPC的目标就是要2~10这些步骤都封装起来,让用户对这些细节透明。工具
实现方式:服务注册中心。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 就是一个注册表,服务端将服务对象放入到注册表中,客户端从注册表中获取服务对象。
客户端的请求消息结构通常须要包括如下内容: /** 1)接口名称:服务端就调用的那个接口; 2)方法名:一个接口内可能有不少方法,若是不传方法名服务端也就不知道调用哪一个方法; 3)参数类型&参数值:参数类型有不少,好比有bool、int、long、double、string、map、list,甚至如struct(class);以及相应的参数值; 4)超时时间 5)requestID,标识惟一请求id */ 服务端返回的消息结构通常包括如下内容。 /** 1)返回值 2)状态code 3)requestID */
客户端怎么把参数值传给远程的函数呢?在本地调用中,咱们只须要把参数压到栈里,而后让函数本身去栈里读就行。
可是在远程过程调用时,客户端跟服务端是不一样的进程,不能经过内存来传递参数。
这时候就须要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成本身能读取的格式。
只有二进制数据才能在网络中传输,序列化和反序列化的定义是:
这个过程叫序列化和反序列化。同理,从服务端返回的值也须要序列化反序列化的过程。
目前互联网公司普遍使用Protobuf、Thrift、Avro等成熟的序列化解决方案来搭建RPC框架,这些都是久经考验的解决方案。
网络传输:远程调用每每用在网络上,客户端和服务端是经过网络链接的。
全部的数据都须要经过网络传输,所以就须要有一个网络传输层。网络传输层须要把 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 协议的数据效率要低,信息传输所占用的时间也会更长,固然压缩数据,可以缩小这一差距。 */