gRPC 是一个高性能、通用的开源RPC框架,其由 Google 主要面向移动应用开发并基于HTTP/2 协议标准而设计,基于 ProtoBuf(Protocol Buffers) 序列化协议开发,且支持众多开发语言。本文作者深入研究了 gRPC 协议,对协议本身作出解构。
gRPC 是基于 HTTP/2 协议的,要深刻理解 gRPC,理解下 HTTP/2 是必要的,这里先简单介绍一下 HTTP/2 相关的知识,然后再介绍下 gRPC 是如何基于 HTTP/2 构建的。
HTTP/1.x
HTTP 协议可以算是现阶段 Web 上面最通用的协议了,在之前很长一段时间,很多应用都是基于 HTTP/1.x 协议,HTTP/1.x 协议是一个文本协议,可读性非常好,但其实并不高效,笔者主要碰到过几个问题:
Parser
如果要解析一个完整的 HTTP 请求,首先我们需要能正确的读出 HTTP header。HTTP header 各个 fields 使用 \r\n
分隔,然后跟 body 之间使用 \r\n\r\n
分隔。解析完 header 之后,我们才能从 header 里面的 content-length
拿到 body 的 size,从而读取 body。
这套流程其实并不高效,因为我们需要读取多次,才能将一个完整的 HTTP 请求给解析出来,虽然在代码实现上面,有很多优化方式,譬如:
-
一次将一大块数据读取到 buffer 里面避免多次 IO read
-
读取的时候直接匹配
\r\n
的方式流式解析
但上面的方式对于高性能服务来说,终归还是会有开销。其实最主要的问题在于,HTTP/1.x 的协议是 文本协议,是给人看的,对机器不友好,如果要对机器友好,二进制协议才是更好的选择。
如果大家对解析 HTTP/1.x 很感兴趣,可以研究下 http-parser,一个非常高效小巧的 C library,见过不少框架都是集成了这个库来处理 HTTP/1.x 的。
Request/Response
HTTP/1.x 另一个问题就在于它的交互模式,一个连接每次只能一问一答,也就是client 发送了 request 之后,必须等到 response,才能继续发送下一次请求。
这套机制是非常简单,但会造成网络连接利用率不高。如果需要同时进行大量的交互,client 需要跟 server 建立多条连接,但连接的建立也是有开销的,所以为了性能,通常这些连接都是长连接一直保活的,虽然对于 server 来说同时处理百万连接也没啥太大的挑战,但终归效率不高。
Push
用 HTTP/1.x 做过推送的同学,大概就知道有多么的痛苦,因为 HTTP/1.x 并没有推送机制。所以通常两种做法:
-
Long polling 方式,也就是直接给 server 挂一个连接,等待一段时间(譬如 1 分钟),如果 server 有返回或者超时,则再次重新 poll。
-
Web-sock