截止期限
解释了如何使用截止时间有效处理不可靠的后端。
截止期限
概述
截止时间用于指定一个时间点,在此时间点之后,客户端不愿再等待服务器的响应。这个简单的概念对于构建健壮的分布式系统非常重要。不进行不必要等待的客户端以及知道何时放弃处理请求的服务器将提高您系统的资源利用率和延迟。
请注意,虽然某些语言API有截止时间(deadline)的概念,但其他语言API使用超时(timeout)的概念。当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++ | C++ 示例 |
Python | Python 示例 |