引言
gRPC让我们可以像本地调用一样实现远程调用,对于每一次的RPC调用中,都可能会有一些有用的数据,而这些数据就可以通过metadata来传递。metadata是以key-value的形式存储数据的,其中key是 string类型,而value是[]string,即一个字符串数组类型。metadata使得client和server能够为对方提供关于本次调用的一些信息,就像一次http请求的RequestHeader和ResponseHeader一样。http中header的生命周周期是一次http请求,那么metadata的生命周期就是一次RPC调用。
使用场景
通过metadata
机制,再加上grpc
是基于http2.0
的,我们可以实现将session id
通过metadata
放入http header
的cookie
字段,
通过添加元数据,向服务器传递特定的信息,例如授权令牌、用户标识、链路追踪ID等。服务器可以使用这些元数据来进行身份验证、授权、跟踪请求等操作。
go中使用metadata
1.proto文件
syntax = "proto3";
option go_package = ".;proto";
service Greeter {rpc SayHello (HelloRequest) returns (HelloReply);
}
//将 sessionid放入 放入cookie中 http协议
message HelloRequest {string name = 1;
}message HelloReply {string message = 1;
}
2.新建metadata
MD类型实际上是map,key是string,value是string类型的切片
type MD map[string] []string
创建的时候可以向创建普通的map一样使用new关键字进行创建:
//第一种方式md := metadata.New( map[string]string{ "key1": "val1","key2": "val2"})//第二种方式key不区分大小写,会被统一转成小写。md := metadata.Pairs("key1" , "val1","key1","val1-2",//"key1" will have map value []string{"val1", "val1-2"}"key2" , "val2" ,)
3.发送metadata
通过metadata.NewOutgoingContext
方法将metadata
数据转化为contex.context
传入参数
package mainimport ("OldPackageTest/grpc_test/proto""context""fmt""google.golang.org/grpc""google.golang.org/grpc/metadata"
)func main() {//streamconn, err := grpc.Dial("127.0.0.1:50051", grpc.WithInsecure())if err != nil {panic(err)}defer conn.Close()c := proto.NewGreeterClient(conn)//md := metadata.Pairs("timestamp", time.Now().Format(timestampFormat))md := metadata.New(map[string]string{"name": "bobby","pasword": "imooc",})ctx := metadata.NewOutgoingContext(context.Background(), md)r, err := c.SayHello(ctx, &proto.HelloRequest{Name: "bobby"})if err != nil {panic(err)}fmt.Println(r.Message)
}
4.接收metadata
通过metadata.FromIncomingContext(ctx)
方法从context
中取出metadata
数据
随后可以通过for range取出数据
package mainimport ("context""fmt""google.golang.org/grpc/metadata""net""google.golang.org/grpc""OldPackageTest/grpc_test/proto"
)type Server struct{}func (s *Server) SayHello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloReply,error) {md, ok := metadata.FromIncomingContext(ctx)if ok {fmt.Println("get metadata error")}if nameSlice, ok := md["name"]; ok {fmt.Println(nameSlice)for i, e := range nameSlice {fmt.Println(i, e)}}return &proto.HelloReply{Message: "hello " + request.Name,}, nil
}func main() {g := grpc.NewServer()proto.RegisterGreeterServer(g, &Server{})lis, err := net.Listen("tcp", "0.0.0.0:50051")if err != nil {panic("failed to listen:" + err.Error())}err = g.Serve(lis)if err != nil {panic("failed to start grpc:" + err.Error())}
}}err = g.Serve(lis)if err != nil {panic("failed to start grpc:" + err.Error())}
}
由于grpc是基于http2.0的,可以看出,其中还有一些http的字段