gin+grpc使用metadata和status
【摘要】 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)