核心概念、架构和生命周期
gRPC 关键概念介绍,以及 gRPC 架构和 RPC 生命周期概述。
核心概念、架构和生命周期
不熟悉 gRPC?请先阅读gRPC 简介。有关特定于语言的详细信息,请参阅您选择的语言的快速入门、教程和参考文档。
概述
服务定义
与许多 RPC 系统一样,gRPC 基于定义服务的思想,指定可以远程调用的方法及其参数和返回类型。默认情况下,gRPC 使用protocol buffers(协议缓冲区)作为接口定义语言 (IDL),用于描述服务接口和有效载荷消息的结构。如果需要,也可以使用其他替代方案。
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string greeting = 1;
}
message HelloResponse {
string reply = 1;
}
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 生命周期部分了解更多关于不同类型 RPC 的信息。
使用 API
从 .proto
文件中的服务定义开始,gRPC 提供 protocol buffer 编译器插件,用于生成客户端和服务端代码。gRPC 用户通常在客户端调用这些 API,并在服务端实现相应的 API。
- 在服务端,服务器实现服务声明的方法,并运行 gRPC 服务器来处理客户端调用。gRPC 基础架构解码传入请求,执行服务方法,并编码服务响应。
- 在客户端,客户端有一个称为 stub(对于某些语言,首选术语是 client)的本地对象,它实现与服务相同的方法。然后客户端可以直接调用本地对象上的这些方法,这些方法会将调用的参数包装到适当的 protocol buffer 消息类型中,将请求发送到服务器,并返回服务器的 protocol buffer 响应。
同步与异步
同步 RPC 调用会阻塞直到服务器响应到达,这是 RPC 力求达到的过程调用抽象最接近的近似。另一方面,网络本质上是异步的,在许多场景下,能够启动 RPC 而不阻塞当前线程非常有用。
大多数语言的 gRPC 编程 API 都提供同步和异步版本。你可以在每种语言的教程和参考文档中找到更多信息(完整的参考文档即将发布)。
RPC 生命周期
在本节中,你将仔细了解 gRPC 客户端调用 gRPC 服务器方法时发生的情况。有关完整的实现细节,请参阅特定于语言的页面。
一元 RPC
首先考虑最简单的 RPC 类型,客户端发送一个请求并获得一个响应。
- 客户端调用 stub 方法后,服务器会收到通知,告知此 RPC 已被调用,并附带客户端为此调用提供的元数据、方法名以及指定的截止时间(如果适用)。
- 然后服务器可以立即发送其自身的初始元数据(必须在任何响应之前发送),或者等待客户端的请求消息。哪个先发生取决于应用程序。
- 服务器收到客户端的请求消息后,会执行必要的任何工作来创建并填充响应。然后将响应(如果成功)连同状态详情(状态码和可选的状态消息)以及可选的尾部元数据一起返回给客户端。
- 如果响应状态为 OK,则客户端收到响应,从而完成客户端的调用。
服务器流式 RPC
服务器流式 RPC 类似于一元 RPC,不同之处在于服务器会返回一个消息流来响应客户端的请求。发送完所有消息后,服务器的状态详情(状态码和可选的状态消息)以及可选的尾部元数据会发送到客户端。这完成了服务器端的处理。客户端在收到所有服务器消息后完成。
客户端流式 RPC
客户端流式 RPC 类似于一元 RPC,不同之处在于客户端向服务器发送一个消息流而不是单个消息。服务器会用单个消息响应(连同其状态详情和可选的尾部元数据),通常是在接收到所有客户端消息之后,但不一定如此。
双向流式 RPC
在双向流式 RPC 中,调用由客户端调用方法发起,服务器接收客户端元数据、方法名和截止时间。服务器可以选择发送其初始元数据,或者等待客户端开始流式传输消息。
客户端和服务端的流处理是应用程序特定的。由于两个流是独立的,客户端和服务器可以按照任何顺序读写消息。例如,服务器可以等待直到接收到客户端的所有消息之后再写入其消息,或者服务器和客户端可以玩“乒乓球”——服务器收到一个请求,然后发送回一个响应,然后客户端根据响应发送另一个请求,依此类推。
截止时间/超时
gRPC 允许客户端指定他们愿意等待 RPC 完成多久,超过该时间后,RPC 将因 DEADLINE_EXCEEDED
错误而终止。在服务端,服务器可以查询特定的 RPC 是否已超时,或者完成 RPC 还有多少时间。
指定截止时间或超时取决于语言:有些语言 API 使用超时(时间长度),有些语言 API 使用截止时间(固定的时间点),并且可能有或没有默认的截止时间。
RPC 终止
在 gRPC 中,客户端和服务器都会独立地、本地地判断调用的成功,并且它们的结论可能不一致。这意味着,例如,RPC 在服务器端可能成功完成(“我已经发送了所有响应!”),但在客户端失败(“响应在我设定的截止时间后才到达!”)。服务器也可能决定在客户端发送完所有请求之前完成。
取消 RPC
客户端或服务器都可以随时取消 RPC。取消会立即终止 RPC,因此不会再进行任何进一步的工作。
警告
取消之前所做的更改不会回滚。元数据
元数据是关于特定 RPC 调用的信息(例如认证详情),其形式为键值对列表,其中键是字符串,值通常是字符串,但也可以是二进制数据。
键不区分大小写,由 ASCII 字母、数字和特殊字符 -
、_
、.
组成,且不能以 grpc-
开头(此为 gRPC 保留)。二进制值键以 -bin
结尾,而 ASCII 值键则不以 -bin
结尾。
用户定义的元数据不被 gRPC 使用,这使得客户端可以向服务器提供与调用相关的信息,反之亦然。
元数据的访问取决于语言。
通道
gRPC 通道提供与指定主机和端口上 gRPC 服务器的连接。它用于创建客户端 stub。客户端可以指定通道参数来修改 gRPC 的默认行为,例如开启或关闭消息压缩。通道具有状态,包括 connected
(已连接)和 idle
(空闲)。
gRPC 处理关闭通道的方式取决于语言。有些语言也允许查询通道状态。