本篇文章属于一篇知识的捡漏和复盘类的文章,主要目的就是为了复盘一下gRPC的相关概念,并剖析其原理,相关知识点和使用大家可以参看之前的几篇文章:
以上的几篇文章都不同程度的讲述和RPC协议相关知识和Google开源的RPC框架gRPC的相关知识,但是可能都比较浅显和不成体系,因此想利用这篇文章系统深入的讲述下gRPC,下面开始:
由上图我们可以看出,使用gRPC通信的基本架构中基本分为五部分,他们分别是:
Protocol Buffers,是Google公司开发的一种数据描述语言,简称protobuf。
特点:
首先要说明的是gRPC是RPC协议是一种实现,是一个框架;Protocol Buffers,是Google公司开发的一种数据描述语言。
gRPC和Protocol Buffers的关系就好比浏览器和HTML的关系,不相互依赖,但是需要相互配合使用,才能达到最好的效果。
Protocol Buffers是一个带有.proto
扩展名的普通文本文件。
协议缓冲区数据被构造为消息,其中每条消息都是一个小的信息逻辑记录,包含一系列称为字段的键值对。这是一个简单的例子:
message Person {
string name = 1;
int32 id = 2;
bool has_ponycopter = 3;
}
一旦你指定了你的数据结构,你就可以使用协议缓冲区编译器protoc
从你的原型定义中以你喜欢的语言生成数据访问类。这些为每个字段提供了简单的访问器,例如name()
and set_name()
,以及将整个结构序列化/解析到原始字节的方法。
在普通的 proto 文件中定义 gRPC 服务,将 RPC 方法参数和返回类型指定为协议缓冲区消息:
// The greeter service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
gRPC 使用protoc
特殊的 gRPC 插件从 proto 文件生成代码:将获得生成的 gRPC 客户端和服务器代码,以及用于填充、序列化和检索消息类型的常规协议缓冲区代码。
一元 RPC,其中客户端向服务器发送单个请求并获得单个响应,就像正常的函数调用一样。
rpc SayHello(HelloRequest) returns (HelloResponse);
服务器流式 RPC,其中客户端向服务器发送请求并获取流以读回一系列消息。客户端从返回的流中读取,直到没有更多消息为止。gRPC 保证单个 RPC 调用中的消息顺序。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
客户端流式 RPC,其中客户端写入一系列消息并将它们发送到服务器,再次使用提供的流。一旦客户端完成了消息的写入,它就会等待服务器读取它们并返回它的响应。gRPC 再次保证了单个 RPC 调用中的消息顺序。
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
双向流式 RPC,双方使用读写流发送一系列消息。这两个流独立运行,因此客户端和服务器可以按照他们喜欢的任何顺序读取和写入:例如,服务器可以在写入响应之前等待接收所有客户端消息,或者它可以交替读取消息然后写入消息,或其他一些读取和写入的组合。保留每个流中消息的顺序。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse)
RPC服务的提供主要包括以上四种服务提供方法。
gRPC 允许客户端指定在 RPC 因错误而终止之前,他们愿意等待 RPC 完成多长时间DEADLINE_EXCEEDED
。在服务器端,服务器可以查询特定的 RPC 是否已超时,或者还剩多少时间来完成 RPC。
指定期限或超时是特定于语言的:一些语言 API 根据超时工作,而一些语言 API 根据期限工作。
在 gRPC 中,客户端和服务器都对调用是否成功做出独立的本地判断,并且它们的结论可能不匹配。这意味着,例如,可能有一个 RPC 在服务器端成功完成但在客户端失败。服务器也可以在客户端发送所有请求之前决定完成。
客户端或服务器都可以随时取消 RPC。取消会立即终止 RPC,以便不再进行任何工作。
众所周知,gRPC是基于HTTP2的,而HTTP2又是一个相对HTTP1.1比较新的概念,因此在探究gRPC原理之前有必要先了解下HTTP2是怎样的。
GET /users/1234/status
)。随着频率的增加,它的寿命很长。接收者可能会建立一个长期存在的流,从而实时连续接收用户状态消息,而不是向 /users/1234/status 端点发出单独的请求。流的主要优点是连接并发,即在单个连接上交错消息的能力。gRPC 引入了三个新概念:通道、远程过程调用 (RPC) 和消息。三者之间的关系很简单:每个通道可能有很多 RPC,而每个 RPC 可能有很多消息。
通道是 gRPC 中的一个关键概念。HTTP/2 中的流支持在单个连接上进行多个并发会话;**通道通过在多个并发连接上启用多个流来扩展这个概念。**从表面上看,频道为用户发送消息提供了一个简单的界面;然而,在引擎盖下,大量的工程投入到保持这些连接的活力、健康和利用上。
通道代表到端点的虚拟连接,实际上可能由许多 HTTP/2 连接支持。RPC 与连接相关联(此关联将在后面进一步描述)。RPC 实际上是普通的 HTTP/2 流。消息与 RPC 相关联并作为 HTTP/2 数据帧发送。更具体地说,消息是在数据帧之上*分层的。*一个数据帧可能有很多 gRPC 消息,或者如果一个 gRPC 消息非常大它可能跨越多个数据帧。
好了,到这里关于gRPC的讲解就差不多了,归根结底,gRPC是一个网络协议,既然是网络协议就难逃网络I/O,因此也正是I/O多路复用成就了HTTP2,进而成就了gRPC,下一篇文章,让我们深入浅出网络I/O模型!
参考文章:
https://www.cncf.io/blog/2018/07/03/http-2-smarter-at-scale/
https://grpc.io/blog/grpc-on-http2/