GRPC的matadata使用


在http請求當中我們可以設置header用來傳遞數據,grpc底層採用http2協議也是支持傳遞數據的,採用的是metadata。 Metadata 對於 gRPC 本身來說透明, 它使得 client 和 server 能爲對方提供本次調用的信息。就像一次 http 請求的 RequestHeader 和 ResponseHeader,http header 的生命週期是一次 http 請求, Metadata 的生命週期則是一次 RPC 調用。

一、簡析

項目源代碼路徑:google.golang.org/grpc/metadata

項目文檔路徑:https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md

以下翻譯自官方文檔

1、創建metadata

MD 類型實際上是map,key是string,value是string類型的slice。

type MD map[string][]string

創建的時候可以像創建普通的map類型一樣使用new關鍵字進行創建:

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不區分大小寫,會被統一轉成小寫。

2、發送metadata

md := metadata.Pairs("key", "val")

// 新建一個有 metadata 的 context
ctx := metadata.NewOutgoingContext(context.Background(), md)

// 單向 RPC
response, err := client.SomeRPC(ctx, someRequest)

更多發送方法見項目文檔

3、接收metadata

利用函數 FromIncomingContext從context中獲取metadata:

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

詳細的使用方法可以參考官方文檔,下面是我寫的一個簡單練手的代碼:

1、proto文件編寫

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;
}

2、server端編寫

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
}

3、client端編寫

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:

  1. Apr 1 20:25:34.227395377

三、實際使用舉例

在項目開發中,我們的某個項目就利用這個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;

四、參考文件

1、 https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章