原文: gRPC and Deadlines

gRPC 与 Deadlines

TL;DR(Too long, Don’t read 的首字母缩写, 也用于句首的消息摘要): 总是设置 deadline . 这篇文章解释了为什么我们建议要有意设置 deadlines, 以及展示了一些代码片断来告诉如何设置.

当你使用 gRPC 时, gRPC 库会负责通信, 编码, 解码以及强制执行 deadline. Deadlines 允许 gRPC 客户端在 RPC 以一个错误 DEADLINE_EXCEEDED 终止之前愿意为 RPC 完成而等待多长时间. 默认情况下 deadline 是一个非常大的数值, 取决于特定语言的实现. 如何指定一个 deadlines 也是取决语言的. 一些语言的 API 使用术语 deadline, RPC 应该在某个固定的时间点就要完成. 其他的语言使用 timeout, 它是一个时间段, 即 RPC time out 了.

通常, 当你不设置 deadline 时, 则所有正在进行请求所持有的资源都将保留, 并且所有请求都有可能达到最大的 timeout .这会导致服务会处于一个耗尽资源的危险中(例如内存), 这会导致服务增加延时, 最坏的情况下, 整个服务都会崩溃掉.

为了避免这种情况, 服务应该指定它们在技术支持上最长的默认的 deadline, 以及客户端应该等到响应不再对它们有用时为止. 对于该服务, 可以像在 .proto 文件中提供注释一样简单. 对于客户端来说, 这涉及设置一个常用的 deadline.

“什么是一个好的 deadline/timeout 值?” 这并没有单一的答案. 你的服务可能像我们的快速开始指南中的 Greeter 那样简单, 这种情况下 100 ms 是个比较好的值. 你的服务可能跟一个全局分布式以及强一致数据库那样复杂. 对于一个客户端查询所设置的 deadline 将会有所不同于应该为删除它们的表要等待多久.

那么你需要考虑如何在 deadline 作出明智的选择? 要考虑的因素包含整个系统的端到端的延迟, 哪些 RPC 是串行的, 哪些是并行的. 你应该能够估算一个大概的数字, 尽管它是一个粗略的计算. 工程师需要了解该服务, 然后为客户端和服务器之间设置一个有意的 deadline

在 gRPC 中, 客户端和服务器都对远程调用 (RPC) 是否成功进行了独立的本地判断. 这意味着它们的结论可能不匹配! 在服务器成功完成的 RPC 可能会在客户端发生故障. 例如,服务器可以发送回复, 但回复可以在 deadline 过期后达到客户端. 客户端已经以状态错误 DEADLINE_EXCEDED 终止. 这应该在应用程序级别检查和管理.

设置一个 deadline

对于客户端来说, 应该总是设置一个 deadline 来表示你将会愿意等待多久从服务器中得到一个回复.

Go

clientDeadline := time.Now().Add(time.Duration(*deadlineMs) * time.Millisecond)
ctx, cancel := context.WithDeadline(ctx, clientDeadline)

java

response = blockingStub.withDeadlineAfter(deadlineMs, TimeUnit.MILLISECONDS).sayHello(request);

检测 deadline

在服务器端, 服务器可以查询一个特定的 RPC 是否不再需要了. 在服务器开始响应之前去检测是否仍有一个客户端在等待它, 这是非常重要的. 这对于开始一些昂贵(译注: 即耗时或耗资源之类的操作)的操作之间特别重要.

Go

if ctx.Err() == context.Canceled {
	return status.New(codes.Canceled, "Client cancelled, abandoning.")
}

Java

if (Context.current().isCancelled()) {
  responseObserver.onError(Status.CANCELLED.withDescription("Cancelled by client").asRuntimeException());
  return;
}