metadata
gRPC Metadata 使用核心指南
一元 RPC (Unary RPC)
客户端发送 Metadata
// 创建初始 Metadatamd := metadata.Pairs("client-id", "12345", "auth-token", "bearer xyz")
// 方式1: 创建新上下文ctx := metadata.NewOutgoingContext(context.Background(), md)
// 方式2: 向现有上下文追加ctx = context.Background()ctx = metadata.AppendToOutgoingContext(ctx, "client-id", "12345")ctx = metadata.AppendToOutgoingContext(ctx, "auth-token", "bearer xyz")
// 发起调用response, err := client.UnaryRPC(ctx, request)服务器接收 Metadata
func (s *server) UnaryRPC(ctx context.Context, req *pb.Request) (*pb.Response, error) { // 获取客户端 Metadata md, ok := metadata.FromIncomingContext(ctx) if ok { // 获取特定值 clientID := md.Get("client-id") authToken := md.Get("auth-token") }
// 发送 Header Metadata header := metadata.Pairs("server-header", "value") grpc.SendHeader(ctx, header)
// 设置 Trailer Metadata trailer := metadata.Pairs("server-trailer", "value") grpc.SetTrailer(ctx, trailer)
return response, nil}客户端接收 Metadata
response, err := client.UnaryRPC(ctx, request)
// 接收 Headerheader, err := grpc.Header(ctx)
// 接收 Trailertrailer := grpc.Trailer(ctx)流式 RPC (Streaming RPC)
客户端发送 Metadata
// 创建带 Metadata 的上下文md := metadata.Pairs("stream-id", "stream-001")ctx := metadata.NewOutgoingContext(context.Background(), md)
// 创建流stream, err := client.StreamRPC(ctx)
// 追加额外 Metadatactx = metadata.AppendToOutgoingContext(ctx, "additional-data", "extra")服务器接收 Metadata
func (s *server) StreamRPC(stream pb.Service_StreamRPCServer) error { // 获取客户端 Metadata md, ok := metadata.FromIncomingContext(stream.Context()) if ok { streamID := md.Get("stream-id") }
// 发送 Header Metadata header := metadata.Pairs("stream-header", "value") stream.SendHeader(header)
// 处理流...
// 设置 Trailer Metadata trailer := metadata.Pairs("stream-trailer", "value") stream.SetTrailer(trailer)
return nil}客户端接收 Metadata
// 创建流后立即接收 Headerheader, err := stream.Header()
// 流结束后接收 Trailertrailer := stream.Trailer()核心方法对比
NewOutgoingContext vs AppendToOutgoingContext
| 方法 | 用途 | 示例 |
|---|---|---|
| NewOutgoingContext | 创建新的带 Metadata 的上下文 | ctx := metadata.NewOutgoingContext(baseCtx, md) |
| AppendToOutgoingContext | 向现有上下文追加 Metadata | ctx = metadata.AppendToOutgoingContext(ctx, "key", "value") |
// 组合使用示例func prepareContext() context.Context { // 初始 Metadata baseMD := metadata.Pairs("request-id", "req-123") ctx := metadata.NewOutgoingContext(context.Background(), baseMD)
// 动态追加 Metadata if authRequired { ctx = metadata.AppendToOutgoingContext(ctx, "authorization", token) }
// 添加时间戳 ctx = metadata.AppendToOutgoingContext(ctx, "timestamp", time.Now().Format(time.RFC3339))
return ctx}最佳实践
- 键命名规范:
// 推荐小写 + 连字符格式 metadata.Pairs("client-id", "123", "auth-token", "xyz")- 二进制数据:
// 使用 -bin 后缀标识二进制数据 binData := []byte{0x01, 0x02}md := metadata.Pairs("binary-header-bin", string(binData))- 错误处理中的 Metadata:
// 服务器端 if err != nil { trailer := metadata.Pairs("error-details", "Invalid input") stream.SetTrailer(trailer) return status.Errorf(codes.InvalidArgument, "invalid request") }- 客户端接收错误详情:
if err != nil { st, _ := status.FromError(err) details := st.Details() trailer := grpc.Trailer(ctx) }关键点总结
-
客户端发送:使用
NewOutgoingContext或AppendToOutgoingContext -
服务器接收:使用
metadata.FromIncomingContext -
服务器发送:
-
Header:
grpc.SendHeader()(Unary) 或stream.SendHeader()(Stream) -
Trailer:
grpc.SetTrailer()(Unary) 或stream.SetTrailer()(Stream)
-
-
客户端接收:
-
Header:
grpc.Header()(Unary) 或stream.Header()(Stream) -
Trailer:
grpc.Trailer()(Unary) 或stream.Trailer()(Stream)
-