背景
- 单向 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)
}
参考文献