gRPC 核心概念和生命周期

创建于 2024年3月27日修改于 2024年5月5日
gRPC

RPC


概述

本篇介绍 gRPC 的核心概念,以及其架构和生命周期。

服务定义

与许多 RPC 系统一样,gRPC 的基础是定义一个服务,指定可以远程调用的方法及其参数和返回类型。默认情况下,gRPC 使用 protocol buffers 作为接口定义语言 (IDL),用于描述服务接口和负载消息的结构。当然,如果想要使用其他替代 IDL 也是可以的。

service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

message HelloRequest {
  string greeting = 1;
}

message HelloResponse {
  string reply = 1;
}

gRPC 允许定义四种类型的服务方法:

rpc SayHello(HelloRequest) returns (HelloResponse);
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);

使用 API

.proto 文件中的服务定义开始,gRPC 提供 protocol buffer 编译器插件来生成客户端和服务端代码。通常,gRPC 用户在客户端调用这些 API,并在服务器端实现相应的 API。

同步 vs. 异步

同步 RPC 调用会阻塞,直到从服务器收到响应。它与普通的本地方法调用最相似。但是,网络本质上是异步的。因此,在许多场景中,能够不阻塞当前线程的情况下启动 RPC 是非常有用的。

大多数语言中的 gRPC 编程 API 都有同步和异步两种风格。你可以在每种语言的教程和参考文档中了解到更多信息。

RPC 生命周期

本节中,你将更详细地了解当 gRPC 客户端调用 gRPC 服务端方法时会发生什么。

一元 RPC

首先考虑最简单类型的 RPC:客户端发送单个请求并返回单个响应。

  1. 客户端调用存根方法后,服务器会知道该 RPC 被调用,并带有客户端元数据、方法名称和指定的截止时间(如果适用的话)。
  2. 然后,服务器可以立即发送自己的初始元数据(必须在任何响应之前发送),或者等待客户端的请求消息。哪个先发生取决于应用程序。
  3. 一旦服务器收到客户端的请求消息,它会执行创建和填充响应所需的所有工作。然后,如果成功,将响应返回给客户端,并附带状态详细信息(状态代码和可选状态消息)和可选的尾部元数据。
  4. 如果响应状态是 OK,客户端将收到响应,即调用完成。

服务端流式 RPC

服务端流式 RPC 类似于一元 RPC,区别在于服务器在响应客户端请求时会返回一系列消息。发送完所有消息后,服务器的状态详细信息(状态代码和可选状态消息)和可选的尾部元数据将发送给客户端。 这表示服务端的处理已完成。客户端在获取到所有的服务器消息后表示处理完成。

客户端流式 RPC

客户端流式 RPC 类似于一元 RPC,区别在于客户端向服务器发送一系列消息,而不是单个消息。服务器响应单个消息(以及其状态详细信息和可选的尾部元数据)。服务器通常是在(但非必须)收到所有客户端的消息后响应。

双向流式 RPC

在双向流式 RPC 中,call(调用)由客户端 invoke(调用)方法发起;服务器接收客户端元数据、方法名称和截止时间。服务器可以选择发送其初始元数据,或者等待客户端开始流式传输消息。

客户端和服务器端的流处理方式是由应用程序自己决定的。由于这两个流是独立的,因此客户端和服务器可以以任何顺序读取和写入消息。例如,服务器可以等待接收所有客户端的消息,然后再写入其消息,或者服务器和客户端可以进行“乒乓球”式的交互 - 服务器收到请求,然后发送响应,然后客户端根据响应发送另一个请求,以此类推。

截止时间/超时

gRPC 允许客户端指定他们愿意等待 RPC 完成的时间,否则 RPC 将以 DEADLINE_EXCEEDED 错误终止。在服务器端,服务器可以查询特定 RPC 是否已超时,或者还剩多少时间来完成 RPC。

指定截止时间或超时是语言特定的:某些语言 API 使用超时(持续时间),而某些语言 API 使用截止时间(固定时间点),并且不一定有设置默认截止时间。

RPC 终止

在 gRPC 中,客户端和服务端都独立地、本地地判定调用是否成功。二者的结论有可能不匹配。例如,你可以在服务端成功完成 RPC(“我已发送了所有响应!”),但在客户端端失败(“响应在我的截止时间之后到达!”)。服务端也可以在客户端发送完所有请求之前决定结束调用。

取消 RPC

客户端或服务器都可以随时取消 RPC。取消将立即终止 RPC,不会再进行进一步的操作。

元数据

元数据是关于特定 RPC 调用的信息(例如身份验证详细信息),以键值对的形式表示,其中键是字符串,值通常也是字符串,但也可以是二进制数据。

键不区分大小写,由 ASCII 字母、数字和特殊字符 -_. 组成,且不能以 grpc- 开头(这是保留给 gRPC 本身的)。二进制值的键必须以 -bin 结尾; ASCII 值键则不需要以此结尾。

用户定义的元数据不会被 gRPC 使用,这允许客户端提供与调用相关的信息给服务器,反之亦然。

对元数据的访问方式取决于各个语言。

通道

gRPC 通道提供与指定主机和端口上的 gRPC 服务器的连接。它在创建客户端存根时使用。客户端可以指定通道参数以修改 gRPC 的默认行为,例如启用或禁用消息压缩。通道具有状态,包括 connected(连接)和 idle(空闲)。

gRPC 关闭通道的方式是语言特定的。某些语言还允许查询通道状态。