截止时间

解释了如何使用截止时间来有效处理不可靠的后端。

截止时间

解释了如何使用截止时间来有效处理不可靠的后端。

概述

截止时间用于指定一个时间点,超过该时间点客户端不愿意等待服务器的响应。这个简单的想法对于构建健壮的分布式系统非常重要。不进行不必要等待的客户端和知道何时放弃处理请求的服务器将提高系统的资源利用率和延迟。

请注意,虽然某些语言 API 具有截止时间的概念,但其他语言使用超时的概念。当 API 要求截止时间时,您提供一个调用不应超过的时间点。超时是调用可以采用的最大持续时间。可以通过将超时添加到应用程序开始调用时的当前时间,将超时转换为截止时间。为简单起见,本文档中我们将仅提及截止时间。

客户端的截止时间

默认情况下,gRPC 不设置截止时间,这意味着客户端最终可能会无限期地等待响应。为了避免这种情况,您应该始终在客户端中显式设置一个合理的截止时间。要确定适当的截止时间,理想情况下,您应该从对系统(网络延迟、服务器处理时间等)的了解开始进行有根据的猜测,并通过一些负载测试进行验证。

如果服务器在处理请求时已超过截止时间,客户端将放弃并使 RPC 失败,状态为DEADLINE_EXCEEDED

服务器端的截止时间

服务器可能会收到客户端发送的 RPC,这些 RPC 的截止时间不切实际地短,以至于服务器永远没有足够的时间及时响应。这将导致服务器仅仅浪费宝贵的资源,在最坏的情况下,导致服务器崩溃。当客户端设置的截止时间已过时,gRPC 服务器通过自动取消调用(CANCELLED 状态)来处理这种情况。

请注意,服务器应用程序负责停止它为服务 RPC 而产生的任何活动。如果您的应用程序正在运行一个长时间运行的进程,您应该定期检查启动它的 RPC 是否已取消,如果已取消,则停止处理。

截止时间传播

您的服务器可能需要调用另一个服务器才能生成响应。在这些情况下,当您的服务器也充当客户端时,您会希望遵守原始客户端设置的截止时间。某些 gRPC 实现支持自动将截止时间从传入的 RPC 传播到传出的 RPC。在某些语言中,此行为需要显式启用(例如 C++),而在另一些语言中,此行为默认启用(例如 Java 和 Go)。使用此功能可以避免为每个传出的 RPC 手动包含截止时间的容易出错的方法。

由于截止时间是设置的时间点,因此按原样将截止时间传播到服务器可能会有问题,因为两个服务器上的时钟可能未同步。为了解决这个问题,gRPC 将截止时间转换为超时,从中已经扣除了已经过去的时间。这可以保护您的系统免受任何时钟偏差问题的影响。

%%{init: { "sequence": { "mirrorActors": false }}}%%
sequenceDiagram
  participant c as Client
  participant us as User Server
  participant bs as Billing Server
  note right of c: Request at 13:00:00<br>Should complete in 2s
  activate c
  c ->> us: GetUserProfile<br>(deadline: 13:00:02)
  activate us
  note right of us: 0.5s spent before<br>calling billing server
  us ->> bs: GetTransactionHistory<br>(timeout: 1.5s)
  activate bs
  bs ->> bs: Retrieve transactions
  note left of bs: It's 13:00:02<br>Time's up!
  note right of c: Stop waiting for server
  c ->> c: Stop waiting for server<br>DEADLINE_EXCEEDED
  deactivate c
  us ->> us: Stop waiting for server
  us -->> c: Cancel
  deactivate us
  bs -->> us: Cancel
  bs ->> bs: Clean up resources<br>(after noticing that the<br>call was cancelled)
  deactivate bs
 

语言支持

语言示例
JavaJava 示例
GoGo 示例
C++
PythonPython 示例

其他资源