gin+grpc使用metadata和status

举报
yd_254179665 发表于 2025/07/22 16:26:41 2025/07/22
【摘要】 gRPC 中的 metadata 是一种键值对(key-value pairs)的集合,用于在客户端和服务器之间传递额外的信息。这些信息可以是认证信息、跟踪信息、请求上下文等。metadata 是不透明的,即 gRPC 本身不会解析这些数据,而是由应用程序负责处理。在客户端,可以通过 metadata.NewOutgoingContext 函数将 metadata 添加到请求上下文中。在服务...

package main

import (
	"context"
	"log"
	"time"

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

func main() {
	// 连接到 gRPC 服务器
	conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()

	// 创建客户端
	client := NewHelloServiceClient(conn)

	// 发送请求
	resp, err := client.SayHello(context.Background(), &HelloRequest{Name: ""})
	if err != nil {
		// 处理 gRPC 错误
		if st, ok := status.FromError(err); ok {
			log.Printf("Error: %v, Code: %v, Message: %v", err, st.Code(), st.Message())
		} else {
			log.Fatalf("error when calling SayHello: %v", err)
		}
	} else {
		log.Printf("Response: %s", resp.Message)
	}
}

gRPC 中的 metadata 是一种键值对(key-value pairs)的集合,用于在客户端和服务器之间传递额外的信息。这些信息可以是认证信息、跟踪信息、请求上下文等。metadata 是不透明的,即 gRPC 本身不会解析这些数据,而是由应用程序负责处理。

在客户端,可以通过 metadata.NewOutgoingContext 函数将 metadata 添加到请求上下文中。

在服务器端,可以通过 metadata.FromIncomingContext 函数从请求上下文中获取 metadata

syntax = "proto3";

package hello;

// 定义服务接口
service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

// 定义请求消息
message HelloRequest {
  string name = 1;
}

// 定义响应消息
message HelloResponse {
  string message = 1;
}

使用 protoc 编译器生成 Go 代码:

protoc --go_out=. --go-grpc_out=. hello.proto
package main

import (
	"context"
	"fmt"
	"log"
	"net"

	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"hello" // 假设生成的代码在 hello 包中
)

// 实现服务接口
type helloServiceServer struct{}

func (s *helloServiceServer) SayHello(ctx context.Context, in *hello.HelloRequest) (*hello.HelloResponse, error) {
	// 从上下文中获取 metadata
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return nil, fmt.Errorf("metadata is not available")
	}

	// 获取特定的 metadata
	authToken := md.Get("auth-token")
	if len(authToken) == 0 {
		return nil, fmt.Errorf("auth-token is missing")
	}

	fmt.Println("Received auth-token:", authToken[0])

	return &hello.HelloResponse{Message: "Hello " + in.Name}, nil
}

func main() {
	// 创建 gRPC 服务器
	lis, err := net.Listen("tcp", ":50051")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()

	// 注册服务
	hello.RegisterHelloServiceServer(s, &helloServiceServer{})

	// 启动服务器
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

客户端代码

package main

import (
	"context"
	"log"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"hello" // 假设生成的代码在 hello 包中
)

func main() {
	// 连接到 gRPC 服务器
	conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()

	// 创建客户端
	client := hello.NewHelloServiceClient(conn)

	// 创建带有 metadata 的上下文
	ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs("auth-token", "123456"))

	// 发送请求
	resp, err := client.SayHello(ctx, &hello.HelloRequest{Name: "World"})
	if err != nil {
		log.Fatalf("error when calling SayHello: %v", err)
	}

	log.Printf("Response: %s", resp.Message)
}

gRPC 定义了一组标准的状态码,用于表示请求的处理结果。这些状态码在 google.golang.org/grpc/codes 包中定义。

常见的状态码包括:

  • OK:操作成功。
  • CANCELLED:操作被取消。
  • UNKNOWN:未知错误。
  • INVALID_ARGUMENT:客户端提供了无效的参数。
  • DEADLINE_EXCEEDED:请求超时。
  • NOT_FOUND:请求的资源未找到。
  • ALREADY_EXISTS:资源已存在。
  • PERMISSION_DENIED:权限不足。
  • RESOURCE_EXHAUSTED:资源耗尽。
  • FAILED_PRECONDITION:操作的先决条件不满足。
  • ABORTED:操作被中止。
  • OUT_OF_RANGE:参数超出范围。
  • UNIMPLEMENTED:方法未实现。
  • INTERNAL:内部错误。
  • UNAVAILABLE:服务不可用。
  • DATA_LOSS:数据丢失。
  • UNAUTHENTICATED:未认证。

 gRPC 服务中,可以使用 google.golang.org/grpc/status 包来创建和处理自定义的错误状态。

package main

import (
	"context"
	"fmt"
	"log"
	"net"

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

// 定义服务接口
type HelloServiceServer interface {
	SayHello(ctx context.Context, in *HelloRequest) (*HelloResponse, error)
}

// 定义请求和响应消息
type HelloRequest struct {
	Name string
}

type HelloResponse struct {
	Message string
}

// 实现服务接口
type helloServiceServer struct{}

func (s *helloServiceServer) SayHello(ctx context.Context, in *HelloRequest) (*HelloResponse, error) {
	// 校验请求参数
	if in.Name == "" {
		return nil, status.Errorf(codes.InvalidArgument, "Name cannot be empty")
	}

	return &HelloResponse{Message: "Hello " + in.Name}, nil
}

func main() {
	// 创建 gRPC 服务器
	lis, err := net.Listen("tcp", ":50051")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	RegisterHelloServiceServer(s, &helloServiceServer{})

	// 启动服务器
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}


【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。