gRPC Metadata 必须通过 metadata.Pairs() 构造并用 grpc.Header()/grpc.Trailer() 显式挂载,服务端须在 handler 入口第一行调用 metadata.FromIncomingContext(ctx) 提取;键名自动转小写且有字符限制,值需 base64 编码二进制内容;经代理或重试时易丢失,需确保 HTTP/2 环境及正确配置。

gRPC Metadata 在 Go 中怎么传才不丢
Metadata 本质是 HTTP/2 的 header,必须在 RPC 开始前或结束时发送;context.Context 只是载体,不是传输通道。直接往 context.WithValue 里塞自定义结构体,服务端根本收不到。
- 客户端必须用
metadata.Pairs()构造metadata.MD,再通过grpc.Header()或grpc.Trailer()显式挂载到调用上 - 服务端用
metadata.FromIncomingContext()从入参ctx提取,不是从context.Background()或任意中间 ctx 提取 - Metadata 键名默认自动转小写(如
"Auth-Token"→"auth-token"),大小写敏感场景要提前统一规范
为什么 server 端 metadata.FromIncomingContext(ctx) 返回空
最常见原因是:没在 handler 函数第一行立刻提取,而是等了中间件、日志、鉴权逻辑之后才调 —— 这期间任何一次 context.WithValue 或 context.WithTimeout 都会切断原始 metadata 链路。
- 务必在 handler 入口第一句就调
md, ok := metadata.FromIncomingContext(ctx) - 不要依赖
ctx.Value()或自己封装的 context 工具函数去“透传” metadata - 如果用了中间件(比如 grpc-zap 日志),确认它没有无意中替换掉
ctx;有些日志库会新建 context 并丢弃原 metadata
metadata.MD 的键值规则和编码限制
gRPC 对 metadata 键值有硬性约束:键必须以 - 或字母开头,只允许小写字母、数字、下划线、横线;值默认按 ASCII 编码,二进制内容需 base64 编码并加后缀 "-bin"(如 "payload-bin")。
- 错误键名:
"X-User-ID"(大写)、"1token"(数字开头)、"user id"(含空格) - 正确写法:
"x-user-id"、"token"、"payload-bin" - 中文或 JSON 字符串建议用
url.QueryEscape或base64.StdEncoding.EncodeToString处理后再塞入
客户端发 Metadata 但服务端收不到的典型配置漏项
Go gRPC 客户端默认启用压缩和流控,但某些代理(如 nginx、envoy)或旧版服务端可能不转发未知 header。Metadata 不是 magic,它依赖底层 HTTP/2 实现的 header 透传能力。
立即学习“go语言免费学习笔记(深入)”;
- 确认服务端监听的是
grpc.Server,不是普通 HTTP server;HTTP/1.1 网关会丢弃所有 gRPC metadata - 若经 nginx,需显式配置
grpc_set_header和grpc_pass_request_headers on - 客户端连接时未禁用重试(
grpc.WithDisableRetry()),重试请求会丢失原始 metadata,除非手动在每次重试前重建










