在golang中,gRPC支持通过header和trailer元数据传输来向请求或响应添加额外的信息。
Header元数据传输
Header元数据是指在请求或响应的头部附加一些额外的信息。例如,在HTTP/2协议中,可以使用header元数据传输一些与认证、授权等相关的信息。
在gRPC中,可以通过生成的客户端stub接口来设置发送到服务器端或者从服务器端接收到的header元数据。具体实现方式如下:
package main
import (
"context"
"log"
pb "path/to/my/service/proto"
"google.golang.org/grpc/metadata"
)
func main() {
conn, err := grpc.Dial(":8888", grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewMyServiceClient(conn)
md := metadata.Pairs(
"Authorization", "Bearer "+accessToken,
"X-Custom-Header", "Value",
)
ctx := metadata.NewOutgoingContext(context.Background(), md)
resp, err := c.Send(ctx, &pb.Request{Message: "World"})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", resp.Reply)
}
上面代码中,我们使用metadata.Pairs()方法创建了一个包含两个header字段的md对象,并将其作为参数传递给metadata.NewOutgoingContext()方法创建出一个带有自定义header元数据的context对象ctx。然后将该ctx对象作为Send()方法调用时的参数,实现了在请求中添加header元数据。
类似的,我们也可以通过metadata.FromIncomingContext()方法从接收到的响应中提取出header元数据。例如:
package main
import (
"context"
"log"
pb "path/to/my/service/proto"
"google.golang.org/grpc/metadata"
)
func main() {
conn, err := grpc.Dial(":8888", grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewMyServiceClient(conn)
resp, err := c.Send(context.Background(), &pb.Request{Message: "World"})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
log.Fatalf("failed to get metadata")
}
for key, values := range md {
for _, value := range values {
log.Printf("%s : %s", key, value)
}
}
log.Printf("Greeting: %s", resp.Reply)
}
上面代码中,我们首先调用Send()方法向服务器发送了一个请求,并接收到相应的响应结果。然后通过metadata.FromIncomingContext()方法从ctx对象中获取到响应头部的metadata信息,并循环遍历所有返回的header字段和值。这样就可以实现在响应中获取header元数据。
Trailer元数据传输
与Header元数据不同,Trailer元数据是指在响应尾部附加一些额外的信息。例如,在HTTP/2协议中,可以使用trailer元数据传输一些关于响应状态、错误信息等的信息。
在gRPC中,可以通过生成的服务端stub接口来设置发送到客户端或者从客户端接收到的trailer元数据。具体实现方式如下:
package main
import (
"context"
"log"
pb "path/to/my/service/proto"
"google.golang.org/grpc/metadata"
)
type myService struct {}
func (s *myService) Send(ctx context.Context, req *pb.Request) (*pb.Response, error) {
md := metadata.Pairs("Trailer-Key", "Value")
grpc.SetTrailer(ctx, md)
return &pb.Response{Reply: "Hello"}, nil
}
func main() {
srv := grpc.NewServer()
pb.RegisterMyServiceServer(srv, &myService{})
if err := srv.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
上面代码中,我们定义了一个名为myService
的服务,并在其Send()方法中使用grpc.SetTrailer()方法设置了一个带有自定义trailer元数据的ctx对象。然后将该ctx对象作为Send()方法调用时的参数,实现了在响应中添加trailer元数据。
类似地,我们也可以通过grpc.SendHeader()方法向客户端发送header元数据。例如:
package main
import (
"context"
"log"
pb "path/to/my/service/proto"
"google.golang.org/grpc/metadata"
)
type myService struct {}
func (s *myService) Send(ctx context.Context, req *pb.Request) (*pb.Response, error) {
md := metadata.Pairs("Trailer-Key", "Value")
grpc.SetTrailer(ctx, md)
grpc.SendHeader(ctx, md)
return &pb.Response{Reply: "Hello"}, nil
}
func main() {
srv := grpc.NewServer()
pb.RegisterMyServiceServer(srv, &myService{})
if err := srv.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
上面代码中,我们在Send()方法中除了设置trailer元数据外,还使用了grpc.SendHeader()方法向客户端发送了一个带有自定义header元数据的ctx对象。这样就可以实现在响应中添加trailer元数据和header元数据。