如何在Golang中发送和接收gRPC客户端服务器头信息

864 阅读1分钟

在这个例子中,我们将在客户端请求和服务器响应中附加头信息。客户端将发送请求头信息,服务器将用头信息进行响应。在这两端,我们将提取头信息。

例子

我不打算显示proto文件,因为它是不相关的。

客户端

package main

import (
	"context"
	"log"
	"strings"
	"time"

	"pkg/proto/user"

	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

func main() {
	log.Println("Client running ...")

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	conn, err := grpc.Dial(":50051", grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		log.Fatalln(err)
	}
	defer conn.Close()

	client := user.NewDeleteServiceClient(conn)

	request := &user.DeleteRequest{Uuid: "UUID-123"}

	// Anything linked to this variable will transmit request headers.
	md := metadata.New(map[string]string{"x-request-id": "req-123"})
	ctx = metadata.NewOutgoingContext(ctx, md)

	// Anything linked to this variable will fetch response headers.
	var header metadata.MD

	response, err := client.Delete(ctx, request, grpc.Header(&header))
	if err != nil {
		log.Fatalln(err)
	}

	xrid := header["x-response-id"]
	if len(xrid) == 0 {
		log.Fatalln("missing 'x-response-id' header")
	}
	if strings.Trim(xrid[0], " ") == "" {
		log.Fatalln("empty 'x-response-id' header")
	}

	log.Println(response.GetCode())
	log.Println(xrid[0])
}

服务器

package main

import (
	"context"
	"log"
	"net"
	"strings"

	"pkg/proto/user"

	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
)

type server struct {
	user.UnimplementedDeleteServiceServer
}

func main() {
	log.Println("Server running ...")

	lis, err := net.Listen("tcp", ":50051")
	if err != nil {
		log.Fatalln(err)
	}

	srv := grpc.NewServer()
	user.RegisterDeleteServiceServer(srv, &server{})

	log.Fatalln(srv.Serve(lis))
}

func (s *server) Delete(ctx context.Context, request *user.DeleteRequest) (*user.DeleteResponse, error) {
	// Anything linked to this variable will fetch request headers.
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return nil, status.Errorf(codes.DataLoss, "failed to get metadata")
	}
	xrid := md["x-request-id"]
	if len(xrid) == 0 {
		return nil, status.Errorf(codes.InvalidArgument, "missing 'x-request-id' header")
	}
	if strings.Trim(xrid[0], " ") == "" {
		return nil, status.Errorf(codes.InvalidArgument, "empty 'x-request-id' header")
	}

	log.Println(request.GetUuid())
	log.Println(xrid[0])

	// Anything linked to this variable will transmit response headers.
	header := metadata.New(map[string]string{"x-response-id": "res-123"})
	if err := grpc.SendHeader(ctx, header); err != nil {
		return nil, status.Errorf(codes.Internal, "unable to send 'x-response-id' header")
	}

	return &user.DeleteResponse{Code: 123}, nil
}

测试

$ go run client/main.go 

2020/04/08 21:50:17 Client running ...
2020/04/08 21:50:17 123
2020/04/08 21:50:17 res-123                        # Test with full header
2020/04/08 21:51:32 missing 'x-response-id' header # Test without header
2020/04/08 21:51:32 empty 'x-response-id' header   # Test with empty header
$ go run server/main.go 

2020/04/08 21:49:35 Server running ...
2020/04/08 21:49:39 UUID-123
2020/04/08 21:49:39 req-123