做彩平的网站,模板网传奇手游,wordpress时光轴插件,wordpress更改登录函数1、metadata
服务间使用 Http 相互调用时#xff0c;经常会设置一些业务自定义 header 如时间戳、trace信息等#xff0c;gRPC使用 HTTP/2
协议自然也是支持的#xff0c;gRPC 通过 google.golang.org/grpc/metadata 包内的 MD 类型提供相关的功能接口。
1.1 类型定义
/…1、metadata
服务间使用 Http 相互调用时经常会设置一些业务自定义 header 如时间戳、trace信息等gRPC使用 HTTP/2
协议自然也是支持的gRPC 通过 google.golang.org/grpc/metadata 包内的 MD 类型提供相关的功能接口。
1.1 类型定义
// MD is a mapping from metadata keys to values. Users should use the following
// two convenience functions New and Pairs to generate MD.
type MD map[string][]stringmetadata.MD 类型的定义非常简单可以像一个普通的 map 一样直接操作同时 metadata 包里封装了很多工
具方法供我们使用。
// 使用 New 方法创建
md : metadata.New(map[string]string{k1:v1, k2, v2})// 直接使用 make 创建
md : make(metadata.MD)// 使用 Pairs 方法创建
md : metadata.Pairs(k1, v1-1, k1, v1-2)// 一些操作
md.Set(key, v1, v2)
md.Append(key, v3)
md.Delete(key)
vals : md.Get(key)1.2 发送与接收
1.2.1 客户端
客户端请求的 metadata 是通过设置 context 使用的metadata 包提供了两个 context 相关的方法设置好
context 后直接在调用 rpc 方法时传入即可
md : metadata.New(map[string]string{k1:v1, k2, v2})// 使用 NewOutgoingContext 初始化一个新的 context
ctx : metadata.NewOutgoingContext(context.Background(), md)// 使用 AppendToOutgoingContext 向 context 追加 metadata
ctx metadata.AppendToOutgoingContext(ctx, k3, v3)客户端接收响应中的 metadata 需要区分普通 rpc 和 stream rpc
// 普通 rpc使用 grpc.Header 方法包装为 CallOption
var md metadata.MD
res, err : client.Ping(ctx, pb.PingRequest{Value: ping}, grpc.Header(md))// stream rpc
stream, err : client.MultiPong(context.Background(), pb.PingRequest{Value: ping})
if err ! nil {log.Fatal(err)
}// 通过 stream 对象的 Header 方法获取
md, err : stream.Header()
if err ! nil {log.Fatal(err)
}1.2.2 服务端
对应客户端请求的 metadata 是使用 context 设置的那么服务端在接收时自然也是从 context 中读取
metadata 包中的 FromIncommingContext 方法就是用来读取 context 中的 metadata数据的
// unary rpc
func (s *PingPongServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PongResponse, error) {// 读取请求metadatamd, ok : metadata.FromIncomingContext(ctx)if ok {log.Printf(Got md: %v, md)}// stream rpc
func (s *PingPongServer) MultiPingPong(stream pb.PingPong_MultiPingPongServer) error {md, ok : metadata.FromIncomingContext(stream.Context())if ok {log.Printf(Got md: %v, md)}服务端设置响应的 metadata 也非常简单只需要调用封装好的 SetHeader 或 SendHeader 方法即可
// unary rpc
func (s *PingPongServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PongResponse, error) {// 读取请求metadatamd, ok : metadata.FromIncomingContext(ctx)if ok {log.Printf(Got md: %v, md)}// SetHeader设置响应 metadatagrpc.SetHeader(ctx, metadata.New(map[string]string{rkey: rval}))// 注意 SendHeader 只能调用一次// grpc.SendHeader(ctx, metadata.New(map[string]string{rkey: rval}))// stream rpc, 调用 stream 的 SetHeader 方法
func (s *PingPongServer) MultiPong(req *pb.PingRequest, stream pb.PingPong_MultiPongServer) error {stream.SetHeader(metadata.New(map[string]string{rkey: rval}))// 注意 SendHeader 只能调用一次// stream.SendHeader(metadata.New(map[string]string{rkey: rval}))1.3 使用案例
1.3.1 proto 文件编写和编译
demo.proto文件内容
syntaxproto3;
package protos;
option go_package ./protos;protos;service Greeter {rpc SayHello(HelloRequest) returns(HelloReply){}
}message HelloRequest {string name 1;
}message HelloReply {string message 1;
}编译生成demo.pb.go 文件
$ protoc --go_outpluginsgrpc:. demo.proto1.3.2 server端
server.go文件的内容
package mainimport (contextflagfmtdemo/protosgoogle.golang.org/grpcgoogle.golang.org/grpc/metadatalognet
)var host 127.0.0.1var (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)}else{fmt.Println(get metadata success: ,md)}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
}1.3.3 client端
client.go文件的内容
package mainimport (contextfmtdemo/protosgoogle.golang.org/grpcgoogle.golang.org/grpc/metadatatime
)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)}
}1.3.4 运行
[rootzsx demo]# go run server.go
listen at :50001
server add:127.0.0.1:50001
get metadata success: map[:authority:[127.0.0.1:50001] content-type:[application/grpc] key1:[val1] key2:[val2] user-agent:[grpc-go/1.53.0]]
key1 from metadata:0 . val1
:authority:[127.0.0.1:50001]
content-type:[application/grpc]
user-agent:[grpc-go/1.53.0]
key2:[val2]
key1:[val1][rootzsx demo]# go run client.go
Reply is : server: Hello# 项目结构
[rootzsx protoc]# tree demo/
demo/
├── client.go
├── demo.proto
├── go.mod
├── go.sum
├── protos
│ └── demo.pb.go
└── server.go1 directory, 6 files