GRPC底层传输协议
Grpc他是由谷歌开发,其中主要有以下特点:
- 高性能 :gRPC 使用基于二进制的协议,并采用 Protocol Buffers 进行高效的消息序列化和反序列化。它使用 HTTP/2 作为底层传输协议,支持多路复用、头部压缩和流等特性,提供了更低的延迟和更高的吞吐量。
- 跨语言支持 :gRPC 提供了多种编程语言的支持,如 C++, Java, Python, Go 等。通过使用 Protocol Buffers 的接口描述语言,可以自动生成客户端和服务端的代码,提供了更好的类型安全性和编译时检查。
其中使用 HTTP/2作为底层传输协议,然后我们使用proto文件生成的Java或者python代码进行服务提供者和客户端调用。那么他内部协议设计是怎样的呢?
在 HTTP/2 中,客户端和服务器端的所有通信都是通过一个 TCP 连接完成的,这个连接可以传送任意数量的双向字节流。
相关术语如下:
流(stream):在一个已建立的连接上的双向字节流。一个流可以携带一条或多条消息。
帧(frame):HTTP/2 中最小的通信单元。每一帧都包含一个帧头,它至少要标记该帧所属的流。
消息(message):完整的帧序列,映射为一条逻辑上的 HTTP 消息,由一帧或多帧组成。这样的话,允许消息进行多路复 用,客户端和服务器端能够将消息分解成独立的帧,交叉发送 它们,然后在另一端进行重新组合。
如图所示,gRPC 通道代表一个到端点的连接,也就是一个 HTTP/2 连接。当客户端应用程序创建 gRPC 通道的时候,它会在幕后创建一个到服务器端的 HTTP/2 连接。在通道创建完成之后,就可以重用它来发送多个到服务器端的远程调用。这些远程调用会映射为 HTTP/2 中的 流。远程调用中的消息以 HTTP/2 帧的形式进行发送,帧可能会携带一 条 gRPC 长度前缀的消息,也可能在 gRPC 消息非常大的情况下,一条消息跨多帧。
请求头信息
以长度作为前缀的消息
流结束标记(end of stream flag,以下简称 EOS 标记),
如图所示。远程调用在客户端发送请求头信息之后就会初始化,然后其中会发送以长度作为前缀的消息,最后发送 EOS 标记,通知收件方请求消息已发送。
当调用 getProduct 方法时,客户端会通过发送下面的请求头信息来初始化调用。
HEADERS (flags = END_HEADERS) :method = POST ➊ :scheme = http ➋ :path = /ProductInfo/getProduct ➌ :authority = abc.com ➍ te = trailers ➎ grpc-timeout = 1S ➏ content-type = application/grpc ➐ grpc-encoding = gzip ➑ authorization = Bearer xxxxxx ➒
❶ 定义 HTTP 方法。对 gRPC 来说,:method 头信息始终为 POST。
❷ 定义 HTTP 模式。如果启用传输层安全协议(Transport Level Security,TLS),就将模式设置为 https,否则设置为 http。
❸ 定义端点路径。对 gRPC 来说,这个值的构造为 /{ 服务名 }/{ 方 法名 }。
❹ 定义目标 URI 的虚拟主机名。
❺ 定义对不兼容代理的检测。在 gRPC 中,这个值必须为 trailers。
❻ 定义调用的超时时间。如果没有指定,服务器端会假定超时时间无 穷大。
❼ 定义 content-type。对 gRPC 来说,content-type 应该以 application/grpc 开头。否则,gRPC 会给出 HTTP 状态为 415(不 支持的媒体类型)的响应。
❽ 定义消息的压缩类型。可选的值是 identity、gzip、deflate、snappy 和 {custom}。
❾ 这是可选的元数据。authorization 元数据用来访问安全的端点。
其它注意点:
- 名称以 : 开头的头信息叫作保留头信息,HTTP/2 要求保留头信息出现在其他头信息之前。
- gRPC 通信中所传递的头信息分为两类:调用定义的头信息 (call-definition header)和自定义元数据。
- 调用定义的头信息是 HTTP/2 预定义的头信息。这些头信息应该在自定义元数据之前发送。
- 自定义元数据是由应用程序层定义的任意一组键–值对。在声明自定义元数据时,需要确保不要使用以 grpc- 开头的名称。在 gRPC 核心中,这被列为保留名字。
当完成对服务器端调用的初始化之后,客户端会以 HTTP/2 数据帧的形式发送以长度作为前缀的消息。如果这条消息不适合放到一个数据帧中,那么它可以跨多个数据帧。请求消息的结束通过在最后一个 DATA 帧上添加 END_STREAM 标记来实现。当因为没有要发送的数据而需要关闭请求流时,必须发送一个带有 END_STREAM 标记的空数据帧:
DATA (flags = END_STREAM) <Length-Prefixed Message>
- 响应消息
响应消息由服务器端生成,用来响应客户端的请求。与请求消息类似, 在大多数场景中,响应消息也包含 3 个主要部分:响应头信息、以长度作为前缀的消息以及 trailer。如果没有发送以长度作为前缀的消息来响应客户端,则响应消息只会包含头信息和 trailer,如图所示:
下面通过同一个示例来介绍响应消息的 HTTP/2 帧序列。当服务器端发送响应消息至客户端时,首先会发送如下所示的响应头信息。
HEADERS (flags = END_HEADERS) :status = 200 ➊ grpc-encoding = gzip ➋ content-type = application/grpc ➌
❶ 表明 HTTP 请求的状态。
❷ 定义消息的压缩类型。可选的值是 identity、gzip、deflate、snappy 和 {custom}。
❸ 定义 content-type。对 gRPC 来说,content-type 应该以 application/grpc 开头。
与请求头信息类似,应用程序层所定义的自定义元数据也可以按照任意键–值对集的形式在响应头信息中进行发送。
服务器端发送完响应头之后,以长度作为前缀的消息就会以 HTTP/2 数据帧的形式在调用中进行发送。与请求消息类似,如果该消息不适合放到一个数据帧中,那么它可以跨多个数据帧。
DATA <Length-Prefixed Message>
如下所示,END_STREAM 标记并不会随数据帧一起发送,而会作为单独的头信息来发送,名为 trailer,最后,通过发送 trailer 来提醒客户端响应消息已发送。trailer 还会携带状态码以及请求的状态信息。
HEADERS (flags = END_STREAM, END_HEADERS) grpc-status = 0 # OK ➊ grpc-message = xxxxxx ➋
❶ 定义 gRPC 状态码。gRPC 会使用一组定义良好的状态码。这些状态码的定义可以在 gRPC 官方文档中找到。
❷ 定义对错误的描述。这是可选的,只有在处理请求出现错误时,才会进行设置。
trailer 会以 HTTP/2 头信息帧的形式进行投递,但会在响应消息结束时发送。响应 EOS 标记就是在 trailer 头信息中设置的 END_STREAM 标记。另外,它还会包含 grpc-status 头信息和 grpc-message 头信息。
在特定的场景中,请求调用可能会立即失败。在这些情况下,服务器端需要发回一个不包含数据帧的响应。因为服务器端只发送 trailer 作为响应,所以这些 trailer 也会以 HTTP/2 头信息帧的形式进行投递,同时会包含 END_STREAM 标记。另外,trailer 会包含下面的头信息。
HTTP 状态:status
内容类型:content-type
状态:grpc-status
状态信息:grpc-message
3. gRPC 通信模式中的消息流
3.1 简单 RPC 模式
在一元 RPC 模式中,gRPC 服务器端和 gRPC 客户端的通信始终只涉及一个请求和一个响应。如图 所示,请求消息包含头信息, 随后是以长度作为前缀的消息,该消息可以跨一个或多个数据帧。 消息最后会添加一个 EOS 标记,方便客户端半关(half-close)连 接,并标记请求消息的结束。在这里,“半关”指的是客户端在自己的一侧关闭连接,这样一来,客户端无法再向服务器端发送消息, 但仍能够监听来自服务器端的消息。只有在接收到完整的消息之后,服务器端才生成响应。响应消息包含一个头信息帧,随后是以长度作为前缀的消息。当服务器端发送带有状态详情的 trailer 头信息之后,通信就会关闭。
3.2 服务器端 RPC 模式
从客户端的角度来说,一元 RPC 模式和服务器端流 RPC 模式具有相同的请求信息流。这两种情况都是发送一条请求消息,主要差异在于服务器端。
在服务器端流 RPC 模式中,服务器端不再向客户端发送一条响应消息,而会发送多条响应消息。服务器端会持续等待,直到接收到完整的请求消息,随后它会发送响应头消息和多条以长度作为前缀的消息,如图所示。在服务器端发送带有状态详情的 trailer 头信息之后,通信就会关闭。
3.3 客户端 RPC 模式
在客户端流 RPC 模式中,客户端向服务器端发送多条消息,服务器端在响应时发送一条消息。
客户端首先通过发送头信息帧来与服务器端建立连接,然后以数据帧的形式,向服务器端发送多条以长度作为前缀的消息,如图所示。最后,通过在末尾的数据帧中发送 EOS 标记,客户端将连接设置为半关的状态。与此同时, 服务器端读取所接收到的来自客户端的消息。在接收到所有的消息之后,客户端发送一条响应消息和 trailer 头信息,并关闭连接。
3.4 双向流 RPC 模式
在这种模式中,客户端和服务器端都会给对方发送多条消息,直到它们关闭连接为止。
在双向流 RPC 模式中,客户端通过发送头信息帧与服务器端建立连接。然后,它们会互发以长度作为前缀的消息,无须等待对方结 束。如图所示,客户端和服务器端会同时发送消息。两者都可以在自己的一侧关闭连接,这意味着它们不能再发送消息了。