截止时间
解释了如何使用截止时间有效处理不可靠的后端。
截止时间
概述
截止时间用于指定客户端不愿等待服务器响应的某个时间点。这个简单的概念在构建健壮的分布式系统中非常重要。不进行不必要的等待的客户端以及知道何时放弃处理请求的服务器将改善系统的资源利用率和延迟。
请注意,虽然有些语言 API 具有**截止时间**的概念,但其他语言 API 使用的是**超时**的概念。当 API 要求提供截止时间时,您需要提供一个该调用不应超过的时间点。超时是指该调用可能花费的最大时间持续时间。通过将超时时间添加到应用启动调用时的当前时间,可以将超时转换为截止时间。为简单起见,本文档中仅使用截止时间。
客户端的截止时间
默认情况下,gRPC 不设置截止时间,这意味着客户端可能无限期地等待响应。为避免这种情况,您应始终在客户端中明确设置一个实际的截止时间。要确定合适的截止时间,理想情况下,您应根据对系统的了解(网络延迟、服务器处理时间等)进行有根据的猜测,并通过一些负载测试进行验证。
如果服务器在处理请求时超过了截止时间,客户端将放弃并以 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
语言支持
语言 | 示例 |
---|---|
Java | Java 示例 |
Go | Go 示例 |
C++ | |
Python | Python 示例 |