項目源代碼路徑:google.golang.org/grpc/metadatagit
項目文檔路徑:https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.mdgithub
如下翻譯自官方文檔golang
MD 類型其實是map,key是string,value是string類型的slice。api
type MD map[string][]string
建立的時候能夠像建立普通的map類型同樣使用new關鍵字進行建立:tcp
md := metadata.New(map[string]string{"key1": "val1", "key2": "val2"})
或者使用Pairs建立,相同的key值會被組合成slice。函數
md := metadata.Pairs( "key1", "val1", "key1", "val1-2", // "key1" will have map value []string{"val1", "val1-2"} "key2", "val2", )
key不區分大小寫,會被統一轉成小寫。測試
md := metadata.Pairs("key", "val") // 新建一個有 metadata 的 context ctx := metadata.NewOutgoingContext(context.Background(), md) // 單向 RPC response, err := client.SomeRPC(ctx, someRequest)
更多發送方法見項目文檔google
利用函數 FromIncomingContext
從context中獲取metadata:spa
func (s *server) SomeRPC(ctx context.Context, in *pb.SomeRequest) (*pb.SomeResponse, err) { md, ok := metadata.FromIncomingContext(ctx) // do something with metadata }
官方測試項目:https://github.com/grpc/grpc-go/tree/master/examples/features/metadata翻譯
詳細的使用方法能夠參考官方文檔,下面是我寫的一個簡單練手的代碼:
syntax = "proto3"; package protos; // The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) { } } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; }
package main import ( "flag" "fmt" "log" "net" pb "github.com/zhanben/go_server/protos" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/metadata" ) var host = "127.0.0.1" var ( ServiceName = flag.String("ServiceName", "hello_service", "service name") Port = flag.Int("Port", 50001, "listening port") ) func main() { flag.Parse() lis, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", *Port)) if err != nil { log.Fatalf("failed to listen: %s", err) } else { fmt.Printf("listen at:%d\n", *Port) } defer lis.Close() s := grpc.NewServer() defer s.GracefulStop() pb.RegisterGreeterServer(s, &server{}) addr := fmt.Sprintf("%s:%d", host, *Port) fmt.Printf("server addr:%s\n",addr) if err := s.Serve(lis); err != nil { fmt.Printf("failed to serve: %s", err) } } // server is used to implement helloworld.GreeterServer. type server struct{} // SayHello implements helloworld.GreeterServer func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { md, ok := metadata.FromIncomingContext(ctx) if !ok { fmt.Printf("get metadata error") } if t, ok := md["timestamp"]; ok { fmt.Printf("timestamp from metadata:\n") for i, e := range t { fmt.Printf(" %d. %s\n", i, e) } } //fmt.Printf("%v: Receive is %s\n", time.Now(), in.Name) return &pb.HelloReply{Message: "Hello " + in.Name}, nil }
package main import ( "fmt" "time" pb "github.com/zhanben/go_client/protos" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/metadata" ) const ( timestampFormat = time.StampNano // "Jan _2 15:04:05.000" ) func main() { conn, err := grpc.Dial( "127.0.0.1:50001", grpc.WithInsecure()) if err != nil { panic(err) } client := pb.NewGreeterClient(conn) md := metadata.Pairs("timestamp", time.Now().Format(timestampFormat)) ctx := metadata.NewOutgoingContext(context.Background(), md) resp, err := client.SayHello(ctx, &pb.HelloRequest{Name: "hello, world"}) if err == nil { fmt.Printf("Reply is %s\n", resp.Message) }else{ fmt.Printf("call server error:%s\n", err) } }
root@localhost go_client # ./client
Reply is Hello hello, world
root@localhost go_server # ./server
listen at:50001
server addr:127.0.0.1:50001
timestamp from metadata:
在項目開發中,咱們的某個項目就利用這個metadata傳入帳戶號,而後在envoy中配置了轉發規則,來實現流量控制,達到灰度發佈的效果。例以下面的配置
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: creationTimestamp: null name: hello namespace: test esourceVersion: "0x000000000001158F" spec: hosts: - rtsub.uxr http: - match: - headers: key_name://matedata中的key值 regex: account=5022222222;.* //exact: account=50222222;
一、 https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md