grpc调用中Deadline的本质是CancellationToken,客户端通过CancellationTokenSource设置超时并传入token实现超时控制,httpClient.Timeout对其无效,服务端需响应context.CancellationToken以避免资源浪费。

gRPC调用中Deadline的本质是CancellationToken
在 C# gRPC 客户端里,Deadline 并不是独立配置项,而是通过 CancellationToken 间接控制的。底层实际由 gRPC Core 的 deadline 机制驱动,但 .NET SDK 将其映射为标准的取消语义 —— 换句话说,你传一个带超时的 CancellationToken,就等效设置了 Deadline。
用CancellationTokenSource设置超时最直接
这是最常用、也最可控的方式。不要试图手动构造 Deadline 对象(它只在服务端或低层 API 中出现),客户端一律走 CancellationToken。
- 创建
CancellationTokenSource并指定超时时间:var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); - 调用时传入
cts.Token:await client.SayHelloAsync(request, cancellationToken: cts.Token); - 如果超时触发,抛出
RpcException,Status.StatusCode为DeadlineExceeded,Status.Detail通常含 “Deadline Exceeded” - 记得在不需要时调用
cts.Cancel()或cts.Dispose(),尤其在异步未完成就丢弃请求时
避免用HttpClient.Timeout替代gRPC Deadline
HttpClient.Timeout 对 gRPC 调用无效 —— 因为 gRPC over HTTP/2 使用长连接和流式语义,HttpClient.Timeout 只影响连接建立或单次请求头发送阶段,无法中断正在进行的流或等待响应的 RPC。
- 设置
HttpClient.Timeout = TimeSpan.FromSeconds(10)后调用client.SayHelloAsync,若服务端卡住 15 秒,客户端仍会挂起,不会自动取消 - 真正生效的只有传入的
cancellationToken参数 - 如果你用
Grpcchannel.ForAddress自建 channel,HttpClient配置仅影响 DNS 解析、TLS 握手等前置环节,不干预 RPC 执行期
服务端也要配合处理Cancellation
客户端设了超时,服务端若不响应取消信号,资源可能持续占用。.NET gRPC 服务默认会把 CancellationToken 注入到方法参数中:
public override async Task SayHello(HelloRequest request, ServerCallContext context) { // context.CancellationToken 会随客户端 Deadline 自动触发 await DoWorkAsync(context.CancellationToken); return new HelloReply { Message = "Hello" }; }
务必在耗时操作(如数据库查询、HTTP 调用、Task.Delay)中传入该 token,并检查 token.IsCancellationRequested 或使用支持 cancel 的重载(如 HttpClient.GetAsync(..., token))。
没处理 cancellation 的服务端,会让客户端超时后还继续执行,既浪费资源,又可能造成状态不一致 —— 这点容易被忽略,但很关键。