微服务架构语境下,我们经常会聊到 RPC协议、RPC框架 等名词,但其实很多同学并没有真正理解这些术语的含义,只是一个模糊的概念。
本身试图用比较间接的语言解释RPC的相关概念,以及我们为什么使用RPC(框架)
RPC,即Remote Procedure Call ,语义是远程过程调用. 简单的说,RPC就是在一个应用程序/服务中像本地函数调用一样去访问网络上的另一个应用程序/服务中的函数。

如果Application-A可以调用Application-B的function-B1函数和Application-C的function-C1函数
从广义上讲,Redis-cli、MySQL客户端与其服务器进行数据存储通信,也可以将其视为RPC。
但是我们一般说的RPC是指像gRPC等约定了通用的调用语法、内容编码格式的框架,以及他们使用的网络传输协议。
讨论RPC主要关注一下三个方面:

RPC的调用语义,通常有以下几类:
网络传输部分,在设计RPC框架时是相对复杂的部分,需要考虑字节流如何分隔报文,是否需要使用异步变成模型,超时丢包如何处理等。
这一部分对RPC框架使用者而言可能不需要了解特别深入。
gRPC是一个高性能的、开源通用的通用RPC框架,也是目前最流程的RPC框架。 关于gRPC的详细内容开源参考gRPC官网
本文主要以gRPC作为一个范例,讨论gRPC是如何实现RPC框架中的调用语义、内容编码、网络传输。以及对比HTTP等协议的优势。
Protocol Buffers 是gRPC最重要的部分之一,它是gRPC调用语义和内容编码的实现基础。
IDL: 接口描述(定义)语言,interface description language。 用于以语言无关的方式描述一个服务/组件的API,
Protocol Buffers: 是Google开发的一种跨语言、跨平台、可扩展的用于序列化数据协议。参考Protocol Buffers
Protobuf中的proto文件就是IDL的一种具体实现。
proto文件示例:
1. syntax = "proto3";
2. package helloworld;
3. // The greeting service definition.
4. service Greeter {
5. // Sends a greeting
6. rpc SayHello (HelloRequest) returns (HelloReply) {}
7. }
8.
9. // The request message containing the user's name.
10. message HelloRequest {
11. string name = 1;
12. }
13.
14. // The response message containing the greetings
15. message HelloReply {
16. string message = 1;
}
开发者在proto文件中定义每一个接口的调用方式、请求包格式、应答包格式。 然后利用工具,将其转换为各种语言的桩代码。
服务侧实现桩代码中定义的接口。
客户端侧则通过桩代码中的函数,调用接口。

1. protobuf 有更高的压缩率
以下面包体为例
{
"UserID": 101,
"UserName": "windeal"
}
如果使用HTTP-Json,则包体的内容会全部打包为string
"{\"UserID\":10001,\"UserName\":\"windeal\"}"
首先,字段名根据其名称的字符数占用对应的字节
其次,数字变为字符串,通常会占用更多的字节。转为string就需要占用3个字节;
如果使用Protobuf的编码Encoding, 字段类型可以使用标号替代,占用的体积更少。 数字也会使用Base 128 Varints编码使得占用的空间体积更小。
2. protobuf 更快的序列化/反序列化效率
因为protobuf的类型明确,解析策略简单(得益于其编码), 因而有更高的序列化和反序列化效率。
3. protobuf的可读性比HTTP-Json差 (缺点)
因为protobuf序列化后是一些二进制字节,而且字段名也被替换为标号了, 因为肉眼是无法识别其含义的。不具备可读性。
Protocol Buffer 解决的是gRPC的调用语义和内容编码的部分。
gRPC用HTTP2协议来进行网络传输。
gRPC底层是支持多种网络通信协议来进行网络传输的,但是目前讨论的gRPC一般都是指基于HTTP2的gRPC。
gRPC为什么选择HTTP2可以考虑以下几个方面
本身的内容较多,后面会考虑单独写一篇文章介绍。
本文主要从RPC 网络传输关注的几个点,简单介绍下HTTP2相比HTTP1.1的优势。
头部压缩
HTTP 1.1 会携带大量的头部,存在大量冗余文本,而且没有较好的压缩方案, 从而导致报文的体积较大。
HTTP2 使用header静态表为高频的header建立索引,这样header就可以向body那样讲header-key转为标号来进行压缩。从而大大减少了传输体积。
多路复用
HTTP/1.1是基于纯文本的,这导致其消息传递是“管道串形化”的:在同一个TCP连接中只有等一个消息完成之后,才能进行下一条消息;否则客户端无法识别收到的Response是属于哪一个Request。
HTTP2 是基于二进制流的, 它可以为每个请求分配一个序列号, 甚至可以请求拆分成不同的帧。 有了序列号服务就可以区分不同的请求和应答。
Protocol Buffer Language Guide
gRPC系列(一) 什么是RPC?
gRPC系列(二) 如何用Protobuf组织内容
gRPC系列(三) 如何借助HTTP2实现传输
Introductionto HTTP/2