在http请求当中我们可以设置header用来传递数据,grpc底层采用http2协议也是支持传递数据的,采用的是metadata。 Metadata 对于 gRPC 本身来说透明, 它使得 client 和 server 能为对方提供本次调用的信息。就像一次 http 请求的 RequestHeader 和 ResponseHeader,http header 的生命周期是一次 http 请求, Metadata 的生命周期则是一次 RPC 调用

type MD

key不区分大小写,会被统一转成小写

  1. type MD map[string][]string
  2. key的类型:
  3. - digits: 0-9
  4. - uppercase letters: A-Z (normalized to lower)
  5. - lowercase letters: a-z
  6. - special characters: -_.

func New(m map[string]string) MD 从给定的键-值映射创建一个MD

  1. md := metadata.New(map[string]string{"key1": "val1", "key2": "val2"})

func Pairs(kv …string) MD pair返回一个由key, value…如果len(kv)是奇数,则成对恐慌

  1. md := metadata.Pairs(
  2. "key1", "val1",
  3. "key1", "val1-2", // "key1" will have map value []string{"val1", "val1-2"}
  4. "key2", "val2",
  5. )
  6. 相同的key值会被组合成slice

func (md MD) Append(k string, vals …string) Append将值添加到键k,而不是覆盖已经存储在该键上的值
func (md MD) Copy() MD
func (md MD) Get(k string) []string
func (md MD) Len() int
func (md MD) Set(k string, vals …string) Set用一个值片设置给定键的值。
func Join(mds …MD) MD 将多个MD重合起来,同key不同value,value会被组合成slice

func NewOutgoingContext(ctx context.Context, md MD) context.Context 创建一个带MD的待传出的context
func FromIncomingContext(ctx context.Context) (md MD, ok bool) 从传入的context获得一个MD

func AppendToOutgoingContext(ctx context.Context, kv …string) context.Context 返回一个新的上下文,其中提供的kv与上下文中任何现有的元数据合并。

客户端发送与接收

发送

有两种方式将元数据发送到服务器。推荐的方法是使用AppendToOutgoingContext将kv对附加到上下文。这可以与上下文上的现有元数据一起使用,也可以不使用。当没有之前的元数据时,添加元数据;当元数据在上下文中已经存在时,kv对被合并。

  1. // create a new context with some metadata
  2. ctx := metadata.AppendToOutgoingContext(ctx, "k1", "v1", "k1", "v2", "k2", "v3")
  3. // later, add some more metadata to the context (e.g. in an interceptor)
  4. ctx := metadata.AppendToOutgoingContext(ctx, "k3", "v4")
  5. // make unary RPC
  6. response, err := client.SomeRPC(ctx, someRequest)
  7. // or make streaming RPC
  8. stream, err := client.SomeStreamingRPC(ctx)

或者,可以使用NewOutgoingContext将元数据附加到上下文。但是,这将替换上下文中的任何现有元数据,因此如果需要,必须小心保存现有元数据。这比使用AppendToOutgoingContext要慢。下面是一个例子:

  1. // create a new context with some metadata
  2. md := metadata.Pairs("k1", "v1", "k1", "v2", "k2", "v3")
  3. ctx := metadata.NewOutgoingContext(context.Background(), md)
  4. // later, add some more metadata to the context (e.g. in an interceptor)
  5. send, _ := metadata.FromOutgoingContext(ctx)
  6. newMD := metadata.Pairs("k3", "v3")
  7. ctx = metadata.NewOutgoingContext(ctx, metadata.Join(send, newMD))
  8. // make unary RPC
  9. response, err := client.SomeRPC(ctx, someRequest)
  10. // or make streaming RPC
  11. stream, err := client.SomeStreamingRPC(ctx)

接收

Unary call

  1. var header, trailer metadata.MD // variable to store header and trailer
  2. r, err := client.SomeRPC(
  3. ctx,
  4. someRequest,
  5. grpc.Header(&header), // will retrieve header
  6. grpc.Trailer(&trailer), // will retrieve trailer
  7. )
  8. // do something with header and trailer

Streaming call

  1. stream, err := client.SomeStreamingRPC(ctx)
  2. // retrieve header
  3. header, err := stream.Header()
  4. // retrieve trailer
  5. trailer := stream.Trailer()

服务端发送与接收

接收

Unary call

  1. func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) {
  2. md, ok := metadata.FromIncomingContext(ctx)
  3. // do something with metadata
  4. }

Streaming call

  1. func (s *server) SomeStreamingRPC(stream pb.Service_SomeStreamingRPCServer) error {
  2. md, ok := metadata.FromIncomingContext(stream.Context()) // get context from stream
  3. // do something with metadata
  4. }

发送

Unary
为了一次性将header和trailer发送给客户端,服务器可以调用模块grpc中的SendHeader和SetTrailer函数。这两个函数将上下文作为第一个参数。它应该是RPC处理程序的上下文或从它派生的上下文:

  1. func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) {
  2. // create and send header
  3. header := metadata.Pairs("header-key", "val")
  4. grpc.SendHeader(ctx, header)
  5. // create and set trailer
  6. trailer := metadata.Pairs("trailer-key", "val")
  7. grpc.SetTrailer(ctx, trailer)
  8. }

Streaming
对于流调用,header和trailer可以使用接口ServerStream中的SendHeader和SetTrailer函数发送

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

举例:

client

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. pb "github.com/zhanben/go_client/protos"
  6. "golang.org/x/net/context"
  7. "google.golang.org/grpc"
  8. "google.golang.org/grpc/metadata"
  9. )
  10. const (
  11. timestampFormat = time.StampNano // "Jan _2 15:04:05.000"
  12. )
  13. func main() {
  14. conn, err := grpc.Dial( "127.0.0.1:50001", grpc.WithInsecure())
  15. if err != nil {
  16. panic(err)
  17. }
  18. client := pb.NewGreeterClient(conn)
  19. md := metadata.Pairs("timestamp", time.Now().Format(timestampFormat))
  20. ctx := metadata.NewOutgoingContext(context.Background(), md)
  21. resp, err := client.SayHello(ctx, &pb.HelloRequest{Name: "hello, world"})
  22. if err == nil {
  23. fmt.Printf("Reply is %s\n", resp.Message)
  24. }else{
  25. fmt.Printf("call server error:%s\n", err)
  26. }
  27. }

service

  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "log"
  6. "net"
  7. pb "github.com/zhanben/go_server/protos"
  8. "golang.org/x/net/context"
  9. "google.golang.org/grpc"
  10. "google.golang.org/grpc/metadata"
  11. )
  12. var host = "127.0.0.1"
  13. var (
  14. ServiceName = flag.String("ServiceName", "hello_service", "service name")
  15. Port = flag.Int("Port", 50001, "listening port")
  16. )
  17. func main() {
  18. flag.Parse()
  19. lis, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", *Port))
  20. if err != nil {
  21. log.Fatalf("failed to listen: %s", err)
  22. } else {
  23. fmt.Printf("listen at:%d\n", *Port)
  24. }
  25. defer lis.Close()
  26. s := grpc.NewServer()
  27. defer s.GracefulStop()
  28. pb.RegisterGreeterServer(s, &server{})
  29. addr := fmt.Sprintf("%s:%d", host, *Port)
  30. fmt.Printf("server addr:%s\n",addr)
  31. if err := s.Serve(lis); err != nil {
  32. fmt.Printf("failed to serve: %s", err)
  33. }
  34. }
  35. // server is used to implement helloworld.GreeterServer.
  36. type server struct{}
  37. // SayHello implements helloworld.GreeterServer
  38. func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
  39. md, ok := metadata.FromIncomingContext(ctx)
  40. if !ok {
  41. fmt.Printf("get metadata error")
  42. }
  43. if t, ok := md["timestamp"]; ok {
  44. fmt.Printf("timestamp from metadata:\n")
  45. for i, e := range t {
  46. fmt.Printf(" %d. %s\n", i, e)
  47. }
  48. }
  49. //fmt.Printf("%v: Receive is %s\n", time.Now(), in.Name)
  50. return &pb.HelloReply{Message: "Hello " + in.Name}, nil
  51. }