kratos 的 metadata 必须绑定 context.context 并经 transport 层自动透传,否则下游无法读取;http 需启用 transport.withserverrequestheader(),grpc 需用 kratos 封装的 client;禁止 context.withvalue;跨协议需手动桥接;goroutine 需显式传 ctx。

Kratos 的 Metadata 不是靠手动塞参数或全局变量传递的,它必须绑定在 context.Context 上,且需经由 transport 层(HTTP/gRPC)自动透传——漏掉任一环节,下游就收不到。
为什么 downstream service 总是读不到元数据?
常见现象是:上游调用 metadata.AppendTo(ctx, "user-id", "123"),下游 metadata.FromServerContext(ctx) 返回空 map。根本原因不是 API 用错,而是上下文没被正确携带过去。
- HTTP 场景下,
Metadata会自动映射为请求头(如X-User-ID: 123),但前提是 transport/http.Server 必须启用transport.WithServerRequestHeader()选项,否则 header 被丢弃 - gRPC 场景下,
Metadata默认走grpc.SendHeader()和grpc.SetHeader(),但若客户端用的是原始grpc.Dial而非 Kratos 封装的client.NewClient(),上下文不会自动注入 - 中间件里若用了
ctx = context.WithValue(ctx, key, val)手动挂值,下游无法通过metadata.FromServerContext()读取——它只认 Kratos 内部约定的metadata.MDKey
如何在 service 层安全地写入和读取 metadata?
别直接操作底层 map,用框架封装好的工具函数,避免 key 冲突或编码不一致。
- 写入(上游):
ctx = metadata.AppendTo(ctx, "trace-id", traceID, "biz-scene", "recommend")—— 多值支持,同 key 可追加多个 string - 读取(下游):
md, ok := metadata.FromServerContext(ctx),再用md.Get("trace-id")或md.Values("biz-scene")获取 - 注意大小写:HTTP header 是 case-insensitive,但 Kratos 的
Metadatakey 默认按原样存储;建议统一小写 + 连字符(如"x-request-id"),避免跨协议歧义 - 敏感字段(如 token、password)不要放
Metadata,它可能被日志或监控系统自动采集;应走专用 auth transport 或自定义 credential flow
跨 transport 协议时 metadata 会丢失吗?
会,但可控。Kratos 的 Metadata 在 HTTP 和 gRPC 间不是无缝转换的,关键看你怎么桥接。
- HTTP → gRPC 调用:必须在 client 端显式把 HTTP header 转成 gRPC metadata,例如在 transport/http.Handler 中调用
grpc_client.Invoke(ctx, ...)前,用metadata.FromClientContext(r.Context())提取,再metadata.AppendTo(grpcCtx, ...)注入 - gRPC → HTTP 调用:同理,需在 server 端从
metadata.FromServerContext(ctx)提取,再通过http.Header.Set()注入 outbound request - 推荐做法:封装一个通用的
TransportContext工具函数,在 gateway 层统一做双向映射,避免每个 handler 重复写
最易被忽略的一点:Kratos 的 Metadata 不会自动跨 goroutine 传播。如果你在 handler 里启了新 goroutine 处理异步逻辑(比如发消息、写日志),必须显式把 ctx 传进去——用 go func(ctx context.Context) { ... }(ctx),而不是捕获外部变量。











