.NET中gRPC服务必须用Grpc.AspNetCore而非已废弃的Grpc.Core;需在Program.cs中先调用AddGrpc()注册服务,再用MapGrpcService<T>()映射路由,缺一不可,否则服务不可达。

直接上 Grpc.AspNetCore,别碰 Grpc.Core——它已被官方标记为 Legacy,.NET 6+ 中不支持 HTTP/2 流控和优先级,在容器或 Kubernetes 下容易出现连接复位、Status(StatusCode=Unavailable) 等静默失败。
服务端必须注册 AddGrpc() 并映射 MapGrpcService<T>()
漏掉任一,服务就“存在但不可达”:没有错误日志,curl 或 grpcurl 直接报 UNAVAILABLE,浏览器访问也 404。
-
AddGrpc()必须在Program.cs的builder.Services阶段调用,负责注入序列化器、拦截器、服务发现等核心组件 -
MapGrpcService<GreeterService>()必须在app阶段调用,否则路由不生效;Minimal Hosting 模型下不能写在UseRouting()之前 - 若同时启用 HTTPS,Kestrel 必须显式设置
HttpProtocols = HttpProtocols.Http2,否则 Windows 上会 fallback 到 HTTP/1.1 并触发PROTOCOL_ERROR - 调试时建议临时开启
EnableDetailedErrors = true,否则客户端只收到泛泛的UNKNOWN错误,无法区分是序列化失败、空引用还是业务异常
客户端连不上?先查网络和协议一致性,不是代码写错了
90% 的 Status(StatusCode=Unavailable, Detail="failed to connect to all addresses") 根本不是 GrpcChannel.ForAddress() 写错,而是底层连不通。
- 服务端是否真在监听?用
netstat -an | findstr :5001(Windows)或lsof -i :5001(macOS/Linux)确认端口处于LISTEN状态 - HTTP 还是 HTTPS?ASP.NET Core 6+ 默认只监听 HTTPS 地址;若服务端没开 HTTPS,客户端必须用
http://+ 显式禁用 TLS:new SocketsHttpHandler { UseProxy = false, Credentials = null } - Docker/K8s 环境下别写
localhost——容器内localhost指自己;应改用host.docker.internal(Docker Desktop)或 Service 名(K8s) - 检查
appsettings.json中Kestrel:Endpoints:Http:Url是否覆盖了默认配置;非标端口(如 5001)必须显式声明,否则 Kestrel 不监听
.proto 编译失败,大概率是构建上下文或命名空间冲突
不是语法错,而是生成阶段卡住:VS 或 dotnet build 报类型不存在、找不到基类、或干脆没生成 .cs 文件。
-
<Protobuf>项中GrpcServices属性不能只写"Both",尤其在 SDK 补丁版本中会跳过生成;明确写"Server"或"Client" - 多个
.proto文件定义同名message(哪怕不同package),C# 命名空间会冲突;必须加option csharp_namespace = "Your.Company.Protos";强制隔离 -
Grpc.Tools版本需与 SDK 兼容:.NET 6+ 推荐2.40.0~2.52.0区间;太新可能缺 MSBuild targets,太旧不支持Nullable上下文 - 生成文件在
obj/下,不提交到 Git;若手动删过obj又没 clean rebuild,可能残留旧缓存导致类型找不到
超时控制不能只靠 CallOptions.Deadline
服务器侧的 Deadline 依赖双方系统时钟同步,且不中断挂起连接;你看到的 TaskCanceledException 往往是 TCP 层超时(几分钟),不是 gRPC 层预期行为。
- 客户端必须传入带毫秒级 timeout 的
CancellationToken:new CancellationTokenSource(5000).Token -
CallOptions.Deadline主要用于服务端做轻量级截止判断(比如 cancel DB query),不能替代本地 token - 流式调用(server streaming / bidirectional)更需注意:单个
CancellationToken控制整个生命周期,中途不可替换 - 通道(
GrpcChannel)应复用,避免频繁创建销毁;但复用通道时,每个调用仍需独立CancellationToken
最易被忽略的是 Kestrel 的 MaxConcurrentUpgradedConnections 和 MaxStreamsPerConnection:默认值偏低,高并发流式场景下会直接拒绝新 stream,现象是客户端收不到响应、无错误日志、连接看似正常。调大它们比改代码更关键。


















