gRPC之metadata(译)

背景

  • 单向 RPC
  • 服务端流式 RPC
  • 客户端流式 RPC单向
  • 双向流 RPC

介绍

grpc 使得我们能像 调用本地函数一样实现远程函数调用,其中的 Metadata 是在一次 RPC 调用过程中关于这次调用的信息。 是 key-value 的形式, 其中 key 是 string 类型, value 也是一组 string。 Metadata 对于 gRPC 本身来说透明, 它使得 client 和 server 能为对方提供本次调用的信息。就像一次 http 请求的 RequestHeader 和 ResponseHeader,http header 的生命周期是一次 http 请求, Metadata 的生命周期则是一次 RPC 调用。

对于 Metadata 的访问需要语言的支持

创建 Metadata

定义: metadata中的 MD 就是一个map, metada 包提供了两个方便的函数生成 MD: New() 和 Pairs

type MD map[string][]string

使用 New 可以方便的生成 metadata

md := metadata.New(map[string]string{"hello": "world", "foo": "bar"})

使用 Pairs 也能方便的生成 metadata

md := metadeta.Pairs{
        "hello", "world",
        "hello", "world1",    // "hello" []{"world","world1"}
        "foo" , "bar",
}

其中所有的 KEY 均会被默认转换成小写, 也就是 “hello” 和 “Hello” 是同一个 key

从 context 中获取 metadata

可以使用 FromIncomingContext 从 context 中获取 metadata

func (s *server) SomeRPC(ctx context.Context, in *pb.SomeRequest) (*pb.SomeResponse, err) {
    md, ok := metadata.FromIncomingContext(ctx)
    // do something with metadata
}

发送和接收 metadata -client side

发送 metadata

为了将 metadata 发送给服务端,需要将 metadata 用 NewContext 将 metadata 封装进 context 中, 在用 context 去调用 RPC

md := metadata.Pairs("key", "val")

// 新建一个有 metadata 的 context
ctx := metadata.NewOutgoingContext(context.Background(), md)

// 单向 RPC
response, err := client.SomeRPC(ctx, someRequest)

// 流式 RPC
stream, err := client.SomeStreamingRPC(ctx)

如果想在该次 RPC 调用被发送之前从 context 中读取该 metadata,可以使用 FromOutgoingContext

收取 metadata

client 端可以收取的 metadata 有 header 和 trailer

单向 RPC 调用

可以用 Header 和 Trailer 两个方法获取 header 和 trailer

var header, trailer metadata.MD // 
r, err := client.SomeRPC(
    ctx,
    someRequest,
    grpc.Header(&header),    // 接收 header
    grpc.Trailer(&trailer),  // 接收 trailer
)

流式 RPC 调用

对于流式 RPC 调用包括以下:

  • 客户端流式 RPC
  • 服务端流式 RPC
  • 双向流模式 RPC

Header 和 trailer 可以直接从 返回的 stream 中通过 Header 和 Trailer 方法获取

stream, err := client.SomeStreamingRPC(ctx)

// 接收 header
header, err := stream.Header()

// 接收 trailer
trailer := stream.Trailer()

因为 ClientStream 接口定义了这两个方法

type ClientStream interface {
    Header() (metadata.MD, error)
    Trailer() metadata.MD
    CloseSend() error
    Stream
}

发送和接收 metadata -server side

接收 metadata

要读取从 client 端发送的 metadata, server 需要从 RPC 的context 中获取。如果是单向 RPC, 可以从 RPC handler 的context 就能用, 对于流式 RPC 调用, server 需要从 stream 中获取context

单向 RPC

func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) {
    md, ok := metadata.FromIncomingContext(ctx)
}

流式 RPC

func (s *server) SomeStreamingRPC(stream pb.Service_SomeStreamingRPCServer) error {
    md, ok := metadata.FromIncomingContext(stream.Context()) // get context from stream
}

发送

单向 RPC

使用 Sendheader 和 SetTrailer 这两个方法能设置 header 和 trailer, 需要传入 context

func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) {
    // 创建并发送 header
    header := metadata.Pairs("header-key", "val")
    grpc.SendHeader(ctx, header)
    // 创建并设置 header
    trailer := metadata.Pairs("trailer-key", "val")
    grpc.SetTrailer(ctx, trailer)
}

流式 RPC

ClientStream 接口定义了 SendHeader 和 SetTrailer 两个方法

func (s *server) SomeStreamingRPC(stream pb.Service_SomeStreamingRPCServer) error {
    // create and send header
    header := metadata.Pairs("header-key", "val")
    stream.SendHeader(header)
    // create and set trailer
    trailer := metadata.Pairs("trailer-key", "val")
    stream.SetTrailer(trailer)
}

参考文献

如果你觉得本文对你有帮助,欢迎打赏!