gRPC 的状态码只有 17 个,业务上要告诉客户端“哪个字段错了”“多久后再试”怎么办?答案是 google.rpc.Status 的 details。这篇看它在线上长什么样、能带多大、跨服务时会不会丢。
线上 trailers 里的 grpc- 开头的头:[grpc-status: 3 grpc-message: %E5%8F%82%E6%95%B0%E4%B8%8D%E5%90%88%E6%B3%95 grpc-status-details-bin: CAMSD+WPguaVsOS4jeWQiOazlRpTCih0eXB…(共 164 字符)]
客户端:code=InvalidArgument message="参数不合法";details 个数 1
ErrorInfo: reason=BAD_NAME domain=demo.example metadata=map[field:name]google.rpc.Status(code、message、details 列表)被序列化后放在 trailer grpc-status-details-bin 里,与 grpc-status、grpc-message 并存。
details 个数 3
ErrorInfo reason=QUOTA
BadRequest 字段 n:n 必须小于 10
BadRequest 字段 msg:不能为空
RetryInfo 建议等待 1.5s
线上 trailers:[grpc-status: 8 grpc-message: %E9%85%8D%E9%A2%9D%E7%94%A8%E5%AE%8C grpc-status-details-bin: CAgSDOmFjemineeUqOWujBpBCih0eXBlLmd…(共 333 字符)]errdetails 包提供 ErrorInfo(原因、域、键值)、BadRequest(字段错误)、RetryInfo(重试建议)等,客户端按类型处理。一个错误可以带多个。
1 KiB:code=Internal details=1 message="大 details";线上 trailers [grpc-status: 13 grpc-message: %E5%A4%A7 details grpc-status-details-bin: CA0SC+WkpyBkZXRhaWxzGr4ICih0eXBlLmd…(共 1497 字符)]
64 KiB:code=Internal details=1 message="大 details";线上 trailers [grpc-status: 13 grpc-message: %E5%A4%A7 details grpc-status-details-bin: CA0SC+WkpyBkZXRhaWxzGsGABAoodHlwZS5…(共 87519 字符)]
1024 KiB:code=Internal details=1 message="大 details";线上 trailers [grpc-status: 13 grpc-message: %E5%A4%A7 details grpc-status-details-bin: CA0SC+WkpyBkZXRhaWxzGsGAQAoodHlwZS5…(共 1398239 字符)]
20480 KiB:code=Unavailable details=0 message="error reading from server: connection error: COMPRESSION_ERROR";线上 trailers []details 在 trailers 里,受头部大小限制(客户端默认约 16 MiB);1 MiB 以内正常,20 MiB 连接出错。不要放大块数据。
status.New(codes.OK).WithDetails 返回的错误:no error details for status with code OK
客户端:code=NotFound message="找不到";Details() 个数 1
不认识的类型得到的是 error:proto: not found
线上 trailers:[grpc-status: 5 grpc-message: %E6%89%BE%E4%B8%8D%E5%88%B0 grpc-status-details-bin: CAUSCeaJvuS4jeWIsBolCh50eXBlLmdvb2d…(共 95 字符)]OK 状态不能带 details;客户端不认识的类型在 Details() 里以 error 的形式出现。
直接 return err → 客户端 code=FailedPrecondition message="B 的错误" details 里的 reason [FROM_B]
status.Convert(err).Err() → 客户端 code=FailedPrecondition message="B 的错误" details 里的 reason [FROM_B]
fmt.Errorf("A: %w", err) → 客户端 code=FailedPrecondition message="A: rpc error: code = FailedPrecondition desc = B 的错误" details 里的 reason [FROM_B]直接返回下游的错误,details 会原样传给上游客户端;用 %w 包装也保留 code 和 details,但 message 会变成整句错误文本。