什么是RPC
下图是客户端调用远端服务的过程:

1、客户端client发起服务调用请求。 2、client stub 可以理解成一个代理,会将调用方法、参数按照一定格式进行封装,通过服务提供的地址,发起网络请求。 3、消息通过网络传输到服务端。 4、server stub接受来自socket的消息 5、server stub将消息进行解包、告诉服务端调用的哪个服务,参数是什么 6、结果返回给server stub。 7、sever stub把结果进行打包交给socket 8、socket通过网络传输消息 9、client slub 从socket拿到消息。 10、client stub解包消息将结果返回给client。
一个RPC框架就是把步骤2到9都封装起来。
关键点 关键点 关键点!!!!!
RPC是远端过程调用,其调用协议通常包含传输协议和序列化协议。
传输协议包含: 如著名的 gRPC 使用的 http2 协议(所以RPC框架也不都是使用自定义的传输协议),也有如dubbo一类的自定义报文的tcp协议。
序列化协议包含: 如基于文本编码的 xml json,也有二进制编码的 protobuf hessian等。
为什么需要RPC
- 首先要明确一点:RPC可以用HTTP协议实现,并且用HTTP是建立在 TCP 之上最广泛使用的 RPC,但是互联网公司往往用自己的私有协议,比如鹅厂的JCE协议,私有协议不具备通用性为什么还要用呢?因为相比于HTTP协议,RPC采用二进制字节码传输,更加高效也更加安全。
- 现在业界提倡“微服务“的概念,而服务之间通信目前有两种方式,RPC就是其中一种。RPC可以保证不同服务之间的互相调用。即使是跨语言跨平台也不是问题,让构建分布式系统更加容易。
- RPC框架都会有服务降级、流量控制的功能,保证服务的高可用。
序列化和I/O模型的优化
通过优化java原生支持的序列化及IO创建的RPC功能,可以通过以下两点进行优化。
- 数据序列化: 什么是序列化?序列化就是编码的过程,把对象或者数据结构转化成二进制字节码的过程。而反序列化就是把二进制字节码转化成数据结构或者对象。只有经过序列化后的数据才能在网络中传输。
- I/O模型:
客户端和服务端的通信依赖Socket I/O。I/O 模型又可以分为:
- 传统的阻塞 I/O(Blocking I/O)
- 非阻塞 I/O(Non-blocking I/O)
- I/O 多路复用(I/O multiplexing)
- 异步 I/O(Asynchronous I/O)
用Protobuf优化数据序列化
图解Protobuf编码以及Protocol Buffer 序列化原理大揭秘。
用Netty优化I/O模型
Netty 正是采用了第三种 I/O多路复用的方法,I/O多路复用对应Reactor模式。Reactor把耗时的网络操作编码交给专门的线程或者线程池去处理。比如下面这张图是Reactor模式示意图。图中mainReactor线程、subReactor线程、work线程这些不同的线程,分别干不同专业的事情,吞吐量自然上去了。

这里要再多说一句异步I/O。
前面提到的三种I/O模型都归属于同步I/O,用户发起I/O请求后需要等待或者轮询内核I/O的完成。PHP中的swoole框架, 一款异步网络通信框架。当时我第一次听到异步I/O的感到很奇怪,因为之前看到有些文章里都有说到异步I/O往往对应的是Proactor模式,而Proactor在Linix中没有很好的实现。那么Swoole是如何实现异步I/O。
这里就要提协程的概念了。协程可以理解为用户态的线程,他有两个特点:1、占用的资源更少。2、所有切换和调度都发生在用户态。Swoole底层就是借鉴了Go语言的协程,而Go语言之所有能受到关注和部分青睐,也是因为他引入了协程。这里特别要推荐知乎专栏里的协程,高并发IO的终级杀器的文章,通过简单的例子帮你理解协程。
RestFul
操作对象即为资源,对资源的四种操作(post、get、put、delete),并且参数都放在URL上,但是不严格的说Http+json、Http+xml,常见的http api都可以称为Rest接口。
RestFul与RPC比较
服务之间通信目前有两种方式RestFul与RPC
通常,在对外开放的接口服务中,使用restful方式。因为restFul采用http通信协议,http相对更规范,更标准,更通用,无论哪种语言都支持http协议。如果你是对外开放API,例如开放平台,外部的编程语言多种多样,你无法拒绝对每种语言的支持,相应的,如果采用http,无疑在你实现SDK之前,支持了所有语言,所以,现在开源中间件,基本最先支持的几个协议都包含RESTful。
分布式系统间通信通方式常包含两个部分,序列化和通信协议。常见的序列化协议包括json、xml、hession、protobuf、thrift、text、bytes等;通信协议比较流行的是http、soap、websockect。RPC通常基于TCP实现,常用框架例如netty。
- RESTful通常采用http+JSON实现。
- RPC是指通信协议采用二进制方式,而不是http;序列化采用JSON,protobuf,hession等形式的。
自定义TCP协议和HTTP协议的区别/选择
RPC自定义TCP协议和HTTP协议的区别
RPC要使用自定义的TCP协议和HTTP协议的区别在于,Http1.1协议的http报文中有太多的废信息,导致传输效率低;使用自定义的则可以缩短报文长度
-
通用定义的http1.1协议的tcp报文包含太多废信息,一个POST协议的格式大致如下
HTTP/1.0 200 OK Content-Type: text/plain Content-Length: 137582 Expires: Thu, 05 Dec 1997 16:00:00 GMT Last-Modified: Wed, 5 August 1996 15:55:28 GMT Server: Apache 0.84 <html> <body>Hello World</body> </html>即使编码协议也就是body是使用二进制编码协议,报文元数据也就是header头的键值对却用了文本编码,非常占字节数。如上图所使用的报文中有效字节数仅仅占约 30%,也就是70%的时间用于传输元数据废编码。当然实际情况下报文内容可能会比这个长,但是报头所占的比例也是非常可观的。
-
那么假如我们使用自定义tcp协议的报文如下

报头占用的字节数也就只有16个byte,极大地精简了传输内容。
==这也就是为什么后端进程间通常会采用自定义tcp协议的rpc来进行通信的原因。==
否认两点:
- http 协议相较于自定义tcp报文协议,增加的开销在于连接的建立与断开。http协议是支持连接池复用的,也就是建立一定数量的连接不断开,并不会频繁的创建和销毁连接。
- http也可以使用protobuf这种二进制编码协议对内容进行编码,因此二者最大的区别还是在传输协议上。
如何选择?
什么场景使用文本协议http 什么时候使用自定义二进制协议?
- http适用短链接,高延迟,大包通讯,频率低,业务层无需维持长连接以及状态的程序 。例如起点阅读网站
- 自定义二进制协议主要用于:长链接,低延迟,小包通讯频率极高,业务层要求长连接以及状态的程序。 例如游戏服务器位置同步 moba类游戏50ms同步一次
另外 协议的选择前后端最好要一致 大部分人使用http协议的原因是因为主流网页游览器支持http协议 而且它有完善的安全机制 例如 token cqs这些 所以本身http这个协议及很完美 后端没得选择 只能用http协议 直连http客户端的后端服务器其实可以使用dubbo grpc调用真正做事的逻辑服务器 追求单机最大负载
参考
http://developer.51cto.com/art/201906/597963.htm