跳转到内容

Kratos 错误、Metadata 与编码

跨协议调用最容易失去语义的是错误和 metadata。Kratos 用带 code/reason/message/metadata 的 Error 作为稳定模型,再把它映射到 HTTP status 或 gRPC status details。

先给答案:跨协议调用最容易失去语义的是错误和 metadata

Section titled “先给答案:跨协议调用最容易失去语义的是错误和 metadata”

跨协议调用最容易失去语义的是错误和 metadata。Kratos 用带 code/reason/message/metadata 的 Error 作为稳定模型,再把它映射到 HTTP status 或 gRPC status details。 正文沿“错误路径 -> Metadata -> 为什么需要独立模型”展开:先确认入口和状态归属,再跟踪控制流或数据流的推进,最后落到对外可观察的结果。

主要失效边界集中在“原地修改 metadata、普通 error、gRPC details 缺失”这些场景。它们破坏的是容量、顺序、并发或生命周期前提;排查时应先确认状态是否仍由正确对象持有,再核对推进条件和清理路径。

business error
|
v
kratos.Error(code, reason, message, metadata)
|
+--> HTTP status / response body
+--> gRPC status + ErrorInfo details
|
client FromError <- wrapped error / grpc status

Error、New、WithCause 和 GRPCStatus 位于 errors/errors.go:22-80。GRPCStatus 将 HTTP code 转为 gRPC code,并把 reason、metadata 放入 errdetails.ErrorInfo::57-64。FromError 支持 Kratos error、gRPC status 和普通 error::126-151。

metadata.Metadata 是 map[string][]string,写入时统一 key 小写,支持 Add、Set、Get、Clone:metadata/metadata.go:10-75。client/server context 分别保存 metadata::78-101;追加 metadata 时先 Clone,避免修改父 context 中的 map::104-125。

HTTP header、gRPC metadata 和业务错误的字段结构不同。如果 handler 直接依赖某个协议,跨协议网关就必须写大量转换代码。Kratos 把错误语义和请求 metadata 提升到 transport 之上,协议层只负责编码。

替代方案:所有服务直接返回 status.Error 或 http.Error。 问题:reason、业务 code 和 metadata 在协议间丢失,错误链也难以保留。 设计:Error 实现 Unwrap/Is,Metadata 采用 clone-on-write,再由 HTTP/gRPC 适配层完成最终编码。

场景 风险 处理
原地修改 metadata 父请求数据被污染 使用 Clone 后再写
普通 error code 语义丢失 FromError 统一转 Unknown
gRPC details 缺失 reason/metadata 丢失 约定使用 ErrorInfo
错误包装 errors.Is 失效 保留 cause 并使用 Unwrap

跨协议错误模型必须同时保留机器可判断字段、用户可读 message 和可扩展 metadata;只传一段字符串无法支撑治理和排障。

面试锚点

  • Kratos Error 如何在 HTTP 和 gRPC 之间保持语义?
  • Metadata 为什么采用 clone-on-write?
  • FromError 如何处理普通 error 和 gRPC status?