前段时间实现内部gRPC框架时,为了实现在服务端拦截器中打印请求及响应的头部信息,便查阅了部分关于元数据的资料。因为中文网络上对于该领域的信息较少,于是在这做了一些简单的总结。
元数据
gRPC的元数据(metadata)是基于HTTP/2头部实现的键值对数据,它通常用来实现gRPC的鉴权、链路跟踪以及自定义头部数据等功能。
gRPC的元数据分为两种类型,分别是Header
及Trailer
。Header
可以由客户端或服务端发送,它在客户端请求数据或服务器响应数据前发送。Trailer
是一种特殊的头部信息,它仅可由服务端发送,且位于发送的数据之后。
客户端处理
在gRPC客户端中,无论是一元调用还是流调用,可以比较简单地通过google.golang.org/grpc/metadata
包提供的AppendToOutgoingContext
或NewOutgoingContext
方法向请求中加入头部元数据,例如以下几种方式:
// 通过metadata创建新的context
md := metadata.Pairs("k1", "v1", "k2", "v2")
ctx := metadata.NewOutgoingContext(ctx, md)
// 或是向context中添加元数据
ctx = metadata.AppendToOutgoingContext(ctx, "k3", "v3")
// ... 通过ctx进行RPC调用
对于服务端返回的响应中的元数据,一元调用与流调用的处理方式就较为不同。对于一元调用,需要提前定义好用于存储元数据的变量,然后在调用时通过grpc.Header
或grpc.Trailer
增加调用的选项:
var header, trailer metadata.MD
resp, err := cli.UnaryCall(ctx, req, grpc.Header(&header), grpc.Trailer(&trailer))
// 处理header或trailer
而对于任意方式的流调用,都可以简单地通过流调用返回流的Header
或Trailer
方法获得元数据:
stream, err := cli.StreamCall(ctx)
header, err := stream.Header()
trailer, err := stream