grpc-metadata

  1. metadata机制
    1. proto文件
    2. client
    3. server

http协议

  • 换行符

对于http协议,我们将验证的消息放在 里面

而在gRPC中也需要有这样的机制进行验证,但显然不能放在.proto文件当中,因为这部分暴露了实际的消息

比如 JWT,cookies,这些写在.proto文件中不好。而且.proto文件中写的是业务逻辑,而不是其他的校验逻辑。

metadata机制

metadata相当于 http中的 请求头,里面携带着鉴权的数据

proto文件

syntax = "proto3";

option go_package = ".;proto";

service Greeter{
  rpc SayHello(HelloRequest) returns (HelloResponse); // Hello 的接口
}


message HelloRequest {
  string name = 1; // 1 是编号,他不是值!
}

message HelloResponse {
  string  message = 1;
}

client

package main

import (
	"context"
	"fmt"
	"go-grpc/grpc_metadata/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/metadata"
	"time"
)

const (
	timestampFormat = time.StampNano
)

// 调用的话 直接 拷贝一份 .proto 文件
// 然后 生成相关的代码  【千万千万不要修改代码!】

func main() {
	conn, err := grpc.NewClient("127.0.0.1:8888", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	c := proto.NewGreeterClient(conn)

	// metadata
	// New 和 Pairs 都可以
	md := metadata.Pairs(
		"timestamp", time.Now().Format(timestampFormat),
		"name", "LiShun",
		"age", "21")

	//md := metadata.New(map[string]string{
	//	"timestamp": time.Now().Format(timestampFormat),
	//})

	// 会返回一个 context.Background()
	ctx := metadata.NewOutgoingContext(context.Background(), md)

	// 调用
	res, err := c.SayHello(ctx, &proto.HelloRequest{
		Name: "LiShun",
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(res.Message)
}

另一种方法

package main

import (
	"context"
	"fmt"
	"go-grpc/grpc_interpretor/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

type CustomCredential struct {
}

func (c *CustomCredential) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
	return map[string]string{
		"appid":  "101010",
		"appkey": "i am a key",
	}, nil
}

// RequireTransportSecurity 传输需不需要安全协议
func (c *CustomCredential) RequireTransportSecurity() bool {
	return false
}

func main() {

	var opts []grpc.DialOption
	opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
	// 传输 metadata的另一种方法
	opts = append(opts, grpc.WithPerRPCCredentials(&CustomCredential{}))

	conn, err := grpc.NewClient("127.0.0.1:8888", opts...)
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	c := proto.NewGreeterClient(conn)
	res, err := c.SayHello(context.Background(), &proto.HelloRequest{
		Name: "LiShun",
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(res.Message)

}

server

package main

import (
	"context"
	"fmt"
	"go-grpc/grpc_metadata/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"log"
	"net"
)

type Server struct {
	proto.UnimplementedGreeterServer // 嵌入这个方法,这样我们就不需要手动实现了
}

// 限制死,context可以限制时间

func (s *Server) SayHello(ctx context.Context, req *proto.HelloRequest) (*proto.HelloResponse, error) {
	// 检验 metadata
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		fmt.Println("get metadata failed", ok)
		return &proto.HelloResponse{Message: "校验未通过"}, nil
	} // 校验未通过

	// 校验通过
	for key, val := range md {
		fmt.Println(key, val)
	}

	return &proto.HelloResponse{
		Message: "hello " + req.Name + " " + md.Get("timestamp")[0],
	}, nil
}

func main() {
	// 1. 实例化一个 grpc 的 server
	g := grpc.NewServer()
	// 2. 注册
	proto.RegisterGreeterServer(g, &Server{})
	// 3. 启动,绑定端口
	lis, err := net.Listen("tcp", "127.0.0.1:8888")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	err = g.Serve(lis)
	if err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}
github