当 gRPC 客户端不需要 RPC 调用的结果的时候,可以向服务端发送 CANCEL 的信号中断执行。而 Deadlines 是超时或者 I/O 异常触发了取消。
Cancellation
当一个 RPC 取消了,服务端应该停止任何继续执行和结束服务端流。通常服务端是客户端的上游,由于取消操作来源于客户端,因此取消操作应该传播到所有正在执行的系统计算。
客户端取消 RPC 调用流程如下:
Deadlines
可以有效处理不可靠的后端服务。
截止时间用于指定客户端不愿意等待服务器响应的时间点。这一点对于健壮的分布式系统非常重要。客户端不会不必要地等待,服务端知道何时放弃处理请求,将提高系统的资源利用率和延迟。
Deadlines on the Client
默认情况下,gRPC 不会设置 deadline, 这意味着客户端会永远等待服务端的响应。为了避免这种情况的发生,所以需要为客户端设置一个弹性的截止时间。
这个截止时间取决于业务的系统,网络延迟和 cpu 的使用等等。当服务端正在处理一个请求,到了截止时间,客户端将会放弃 RPC的结果,返回 DEADLINE_EXCEEDED
。
Deadlines on the Server
服务端可能接受来自客户端设置不合理的截止时间,不会给服务端充足的时间响应,这将浪费服务端的资源。最坏的情况,将会导致服务端崩溃。gRPC 服务端通过在客户端设置的截止时间过后自动取消调用( CANCELLED 状态)来处理这种情况。
Deadline Propagation
你的服务端可能调用其他的服务返回响应。在这种场景下,你的服务端扮演者客户端的角色,你需要为客户端的超时设置原始客户端的超时时间,在 gRPC 的一些语言中已经支持自动传播超时时间。 由于deadline 是一个时间点,如果 2 台服务器的时间不同步,那么时间传播也会出现问题。需要解决这个问题,请将截止时间转换为已扣除已用时间的超时。这可以保护您的系统免受任何时钟偏差问题的影响。
// 设置固定的时间点 (不推荐,服务器时间不同步问题)
clientDeadline := time.Now().Add(time.Duration(*deadlineMs) * time.Millisecond)
ctx, cancel := context.WithDeadline(ctx, clientDeadline)
// 超时时间设置
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
// 取消
defer cancel()
// 检查是否被取消
if ctx.Err() == context.Canceled {
return status.New(codes.Canceled, "Client cancelled, abandoning.")
}
总结
取消和超时设置在系统设计中非常重要,在工作中也遇到查询数据,调用接口等需要设置超时。在开发过程中,没有注意到数据库里数据的增加或者外部接口的改动,导致接口耗时很高。所以超时设置是很有必要的。