GRPC的metadata使用

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

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
}



1.客户端请求中携带 metadata
在 go 语言中,可以用 grpc.WithPerRPCCredentials 方法来实现。

func WithPerRPCCredentials(creds credentials.PerRPCCredentials) DialOption

通常来说,认证信息是需要每次都携带,但如果需要单次携带 metadata,可以使用 metadata.NewOutgoingContext 方法来创建一个携带 metadata 的 context。
Step1. 实例准备
方法的入参是 PerRPCCredentials 接口,因此需要准备一个实例,实现接口的方法。

type PerRPCCredentials interface {
	GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
	RequireTransportSecurity() bool
}

Step2. 通过 DialOption 新建客户端

 opts = append(opts, grpc.WithPerRPCCredentials(new(customCredential)))
    conn, err := grpc.Dial(address, opts...)

2.服务端中解析 metadata
从 RPC 消息的上下文中获取 metadata

func (s *server) SomeRPC(ctx context.Context, in *pb.SomeRequest) (*pb.SomeResponse, err) {
    md, ok := metadata.FromIncomingContext(ctx)
    // do something with metadata
}

Demo:

1.proto 文件编写:
demo.proto

syntax="proto3";
package protos;

service Greeter {
  rpc SayHello(HelloRequest) returns(HelloReply){}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

执行:$ protoc --go_out=plugins=grpc:. demo.proto 生成demo.pb.go 文件

2.server端
server.go

package main

import (
	"awesomeProject1/metadata/protos"
	"context"
	"flag"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"log"
	"net"
)

var host = "127.0.0.1"

var (
	ServiceName = flag.String("ServiceName","hello_service","service name")
	Port = flag.Int("Port",50001,"listening port")
)

type server struct {
}

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()

	protos.RegisterGreeterServer(s, &server{})
	addr := fmt.Sprintf("%s:%d",host,*Port)
	fmt.Printf("server add:%s\n",addr)

	if err := s.Serve(lis); err != nil {
		fmt.Printf("failed to server: %s",err)
	}
}


func (s *server) SayHello (ctx context.Context,in *protos.HelloRequest) (*protos.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)
		}
	}
	if t1,ok1 := md["key1"]; ok1 {
		fmt.Printf("key1 from metadata:\n")
		for i,e := range t1 {
			fmt.Printf(" %d . %s\n",i,e)
		}
	}
	if len(md) > 0 {
		for k,v := range md {
			fmt.Printf("%v:%v\n",k,v)
		}
	}
	return &protos.HelloReply{Message:"server: "+ in.Name},nil
}

3.client端
client.go

package main

import (
	"awesomeProject1/metadata/protos"
	"context"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"time"
)

const (
	timestampFormat = time.StampNano
)

func main() {
	conn,err := grpc.Dial("127.0.0.1:50001",grpc.WithInsecure())
	if err != nil {
		panic(err)
	}

	client := protos.NewGreeterClient(conn)
	md := metadata.Pairs("timestamp",time.Now().Format(timestampFormat))
	md = metadata.New(map[string]string{"key1":"val1","key2":"val2"})
	ctx := metadata.NewOutgoingContext(context.Background(),md)
	resp,err := client.SayHello(ctx,&protos.HelloRequest{Name:"Hello"})
	if err == nil {
		fmt.Printf("Reply is : %s\n",resp.Message)
	} else {
		fmt.Printf("call server error:%s\n",err)
	}
}

4.运行
进入文件目录
执行:

go run server.go
go run client.go

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值