在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

最新文章

  1. FineReport中hadoop,hive数据库连接解决方案
  2. leetcode-【中等题】2. Add Two Numbers
  3. .PRT extension and multiple NX versions
  4. 让禅道也可以玩BearyChat
  5. Ajax 是什么?Ajax 的交互模型?同步和异步的区别?如何解决跨域问题?以及 HTTP状态码
  6. DNSPod各个套餐的DNS地址
  7. Java序列化与反序列化(Serializable)
  8. CTSC&APIO2017
  9. 1px解决方案--集锦
  10. 竞赛常用STL备忘录
  11. 【数学】NOIP数论内容整理
  12. Java面向对象的基本概念(对象、封装、继承、多态、抽象、接口、泛型)
  13. mysql连接池不能回避的wait timeout问题(转)
  14. win10常见问题处理办法
  15. Gravitee.io alert 引擎架构
  16. [转]关闭WIN7“程序兼容性助理”
  17. InputStream转换为String, byte[] data = new byte[1024]详解
  18. loli的搜索测试-4
  19. JSTL中c:forEach循环里的值的substr操作及对String操作的常用API
  20. 菜单项onCreateOptionsMenu()和onOptionsItemSelected()的使用

热门文章

  1. JS面试题-<变量和类型>-JavaScript浅拷贝与深拷贝
  2. css3关于body的默认滑动机制
  3. php session获取不到的解决方法
  4. sublime3中运行python文件
  5. 高德地图3D菱形 区域点击搜索
  6. 【SpringBoot | Redis】SpringBoot整合Redis
  7. 逆向libbaiduprotect(二)
  8. AsyncDisplayKit编译和使用注意事项
  9. opencv MatchTemplate()模板匹配寻找最匹配部分
  10. PL真有意思(三):名字、作用域和约束