02-gRPC概念

本文档介绍了一些关键的gRPC概念,概述了gRPC的体系结构和RPC生命周期。

假设已阅读了上文, 有关特定语言的详细信息,请参阅所选语言的快速入门,教程和参考文档(如果有)(完整的参考文档即将推出)。

0.1. 概述

0.1.1. 服务定义

与许多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);
  • 客户端侧流数据PC,客户端多次使用提供的流写入一系列消息并将其发送到服务端。 一旦客户端写完消息,它就等待服务端读取并返回响应,gRPC保证在单个RPC调用中的消息的顺序。
  rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) ;
  • 双向流数据RPC,双方使用读写流发送一系列消息,这两个流独立运行,因此客户端和服务端可以按照自己喜欢的顺序进行读写(例如,服务端可以在写入响应之前等待接收所有客户端消息,或者它可以交替地读取消息然后写入消息,或者其他一些读写组合)。gRPC保证每个流中的消息顺序。
  rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);

我们将在下面的RPC生命周期部分中更详细地介绍不同类型的RPC。

使用API接口

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

  • 服务端实现服务定义中声明的方法,并运行gRPC服务器来处理客户端调用。gRPC框架解码传入请求,然后执行服务端方法并将服务端的响应信息编码后返回。
  • 客户端有一个称为存根(stub)的本地对象(对于某些语言,也称为client),它实现与服务端相同的方法,然后,客户端可以仅在本地对象上调用这些方法,并将调用方法时的参数包装在合适的protocol buffers消息类型中,gRPC框架将包装好的请求发送到服务端并接收服务端protocol buffers响应的返回消息。

同步与异步

同步RPC调用将会阻塞直到收到从服务端返回的响应,这是最接近RPC所期望的过程调用抽象。网络本质上是异步的,在许多情况下,能够在不阻塞当前线程的情况下启动RPC将非常有用。

大多数语言的gRPC编程接口都有同步和异步两种版本。可以在每种语言的教程和参考文档中找到更多信息(完整的参考文档即将推出)。

RPC生命周期

现在让我们仔细看看当gRPC客户端调用gRPC服务端方法时会发生什么。我们不会查看实现细节,您可以在我们特定语言的页面中找到有关这些内容的更多信息。

简单RPC

首先让我们看一下最简单的RPC类型,客户端发送单个请求并返回单个响应。

  1. 客户端在stub/client对象上调用方法后,将通知服务端此RPC调用已被激活,通知中将包含此次调用时客户端的元数据,方法名称和指定的截止时间(如果指定)。
  2. 服务端可以立即返回自己的初始元数据(必须在返回任何响应数据之前发送),或者等待客户端的请求消息(首先由应用程序指定)。
  3. 一旦服务端收到客户端的请求消息,它就会执行创建和填充其返回响应所需的所有工作。然后将响应与状态详细信息(状态代码和可选状态消息)以及可选的尾随元数据一起返回(如果成功)到客户端。
  4. 如果状态为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,取消将立即终止RPC,以便不再进行进一步的工作。取消操作并不是“撤消”,所以在取消操作之前所做的更改都将不会被回滚。

元数据

元数据是关于特定RPC操作的信息(例如,身份验证详细信息),它以键值对列表的形式存在,其中键是字符串,值通常也是字符串(但可以是二进制数据)。元数据对于gRPC来说,本身也是不透明的,它允许客户端在调用时提供相关信息给服务端,反之亦然。

对元数据的访问取决于编程语言。

通道

gRPC通道提供与特定主机和端口上的gRPC服务端的一个连接,通常在创建客户端stub/client时使用。客户端可以指定通道参数来修改gRPC的默认行为,例如打开和关闭消息压缩。通道具有状态,包括已连接空闲

gRPC如何处理关闭通道与编程语言有关。某些编程语言还允许查询通道状态。

上次修改: 14 April 2020