【GO开发工程师】grpc进阶#golang
推荐个人主页:席万里的个人空间
文章目录
- 【GO开发工程师】grpc进阶#golang
- 1、protobuf
- 2、grpc
- 2.1、gRPC 的 Metadata机制
- 2.2、grpc拦截器
1、protobuf
syntax = "proto3"; // 指定使用的 protobuf 版本为 proto3
import "google/protobuf/timestamp.proto"; // 导入 Google 提供的时间戳类型定义
option go_package = ".;proto_bak"; // 生成的 Go 语言代码的包名设定为当前目录下的 proto_bak
service Greeter { // 定义一个名为 Greeter 的服务
rpc SayHello (HelloRequest) returns (HelloReply); // 在 Greeter 服务中定义一个远程过程调用 SayHello,接收 HelloRequest 参数,返回 HelloReply 结果
}
enum Gender { // 定义一个枚举类型 Gender
MALE = 0; // 枚举值 MALE,值为 0
FEMALE = 1; // 枚举值 FEMALE,值为 1
}
message HelloRequest { // 定义消息类型 HelloRequest
string name = 1; // 姓名字段,用于存储姓名信息,字段标签为 1
string url = 2; // URL 字段,用于存储 URL 信息,字段标签为 2
Gender g = 3; // Gender 类型的字段,用于存储性别信息,字段标签为 3
map<string, string> mp = 4; // 字符串键值对映射字段,用于存储键值对信息,字段标签为 4
google.protobuf.Timestamp addTime = 5; // 时间戳字段,用于存储添加时间信息,字段标签为 5,使用了导入的时间戳类型定义
}
message HelloReply { // 定义消息类型 HelloReply
string message = 1; // 消息字段,用于存储消息内容,字段标签为 1
}
2、grpc
2.1、gRPC 的 Metadata机制
gRPC 的 Metadata 机制是一种用于在 gRPC 请求和响应中传递元数据的机制。元数据是键值对的集合,可以包含请求的附加信息,比如身份验证凭据、请求标识符、客户端信息等。
在 gRPC 中,元数据可以分为两类:
-
请求元数据(Request Metadata):这些是客户端在发起 gRPC 请求时发送的元数据。它们可以用于传递关于请求的附加信息,比如身份验证凭据、请求标识符、客户端信息等。
-
响应元数据(Response Metadata):这些是服务端在响应 gRPC 请求时发送的元数据。它们可以用于传递关于响应的附加信息,比如服务端处理结果、附加的状态信息等。
元数据以键值对的形式存在,键是字符串,值可以是任意的序列化数据。在 gRPC 中,元数据通常使用 HTTP/2 格式编码,并在 HTTP/2 标头中传输。
Metadata 机制的使用使得 gRPC 具有灵活的扩展性和自定义能力。开发者可以根据自己的需求,在请求和响应中传递任意类型的元数据,以实现各种功能,比如身份验证、流量控制、跟踪、日志记录等。
2.2、grpc拦截器
我想在每一个 RPC 方法的前面或后面做某些操作,我想针对某个业务模块的 RPC 方法进行统一的特殊处理,我想对 RPC 方法进行鉴权校验,我想对 RPC 方法进行上下文的超时控制,我想对每个 RPC 方法的请求都做日志记录,怎么做呢?
gRPC 的拦截器(Interceptors)是一种强大的功能,它允许在 gRPC 请求处理过程中注入自定义逻辑,类似于中间件。拦截器可以用于实现各种功能,如身份验证、日志记录、性能监控、流量控制等。在 gRPC 中,拦截器是通过使用 gRPC 的拦截器接口来实现的。
以下是一些常见的 gRPC 拦截器:
-
认证拦截器(Authentication Interceptor):用于对 gRPC 请求进行身份验证,以确保请求的安全性和合法性。
-
授权拦截器(Authorization Interceptor):用于在请求处理之前验证客户端是否具有执行请求操作的权限。
-
日志记录拦截器(Logging Interceptor):用于记录请求和响应的日志,以便进行故障排查、审计和性能分析。
-
性能监控拦截器(Performance Monitoring Interceptor):用于监控 gRPC 服务的性能指标,如请求处理时间、请求速率等。
-
流量控制拦截器(Traffic Control Interceptor):用于限制客户端请求的速率或配额,以确保服务端不会被过多的请求压力影响。
-
异常处理拦截器(Exception Handling Interceptor):用于捕获并处理 gRPC 服务中的异常情况,以提供更好的错误处理和用户体验。
有关go-grpc-middleware的使用:go-grpc-middleware下载地址
MD和拦截器案例
client.go
package main
import (
"OldPackageTest/grpc_test/proto" // 导入 protobuf 自动生成的代码包
"context" // 导入 context 包
"fmt" // 导入 fmt 包
"google.golang.org/grpc" // 导入 gRPC 包
)
// customCredential 是一个自定义的 gRPC 凭据类型
type customCredential struct{}
// GetRequestMetadata 实现了凭据接口中的 GetRequestMetadata 方法,
// 用于返回请求所需的元数据。
func (c customCredential) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
// 返回一个包含应用程序 ID 和密钥的元数据
return map[string]string{
"appid": "101010",
"appkey": "i am key",
}, nil
}
// RequireTransportSecurity 实现了凭据接口中的 RequireTransportSecurity 方法,
// 返回该凭据是否需要传输安全性。
func (c customCredential) RequireTransportSecurity() bool {
return false // 此示例中不需要传输安全性
}
func main() {
// 创建 gRPC 客户端连接选项数组
var opts []grpc.DialOption
// 添加使用不安全连接的 Dial 选项
opts = append(opts, grpc.WithInsecure())
// 添加使用自定义凭据的 Dial 选项
opts = append(opts, grpc.WithPerRPCCredentials(customCredential{}))
// 连接到 gRPC 服务器
conn, err := grpc.Dial("127.0.0.1:50051", opts...)
if err != nil {
panic(err)
}
defer conn.Close() // 延迟关闭连接
// 创建一个 GreeterClient 实例
c := proto.NewGreeterClient(conn)
// 向服务器发送 SayHello 请求,并传递 HelloRequest 参数
r, err := c.SayHello(context.Background(), &proto.HelloRequest{Name: "bobby"})
if err != nil {
panic(err)
}
fmt.Println(r.Message) // 打印服务器返回的消息
}
helloworld.proto
syntax = "proto3";
option go_package = ".;proto";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
//将 sessionid放入 放入cookie中 http协议
//这个就好比文档,表单验证
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
//go语言中是生成一个文件, 也就只有python会生成两个文件
使用protoc生成go文件:
protoc --go_out=./ *.proto
server.go
package main
import (
"context"
"fmt"
"google.golang.org/grpc/codes" // 导入 gRPC 错误码包
"google.golang.org/grpc/metadata" // 导入 gRPC 元数据包
"google.golang.org/grpc/status" // 导入 gRPC 状态包
"net" // 导入网络包
"google.golang.org/grpc" // 导入 gRPC 包
"OldPackageTest/grpc_test/proto" // 导入 protobuf 自动生成的代码包
)
// Server 定义 gRPC 服务端结构体
type Server struct{}
// SayHello 实现了 Greeter 服务中的 SayHello 方法
func (s *Server) SayHello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloReply,
error) {
// 根据请求参数构造响应消息
return &proto.HelloReply{
Message: "hello " + request.Name,
}, nil
}
func main() {
// 定义拦截器函数
interceptor := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
fmt.Println("接收到了一个新的请求") // 打印日志,表示接收到新的请求
md, ok := metadata.FromIncomingContext(ctx) // 从上下文中获取请求的元数据
fmt.Println(md)
if !ok {
// 如果无法获取元数据,则返回未认证错误
return resp, status.Error(codes.Unauthenticated, "无token认证信息")
}
var (
appid string // 定义应用程序 ID
appkey string // 定义应用程序密钥
)
// 从元数据中提取应用程序 ID 和密钥
if va1, ok := md["appid"]; ok {
appid = va1[0]
}
if va1, ok := md["appkey"]; ok {
appkey = va1[0]
}
// 检查应用程序 ID 和密钥是否有效,若无效则返回未认证错误
if appid != "101010" || appkey != "i am key" {
return resp, status.Error(codes.Unauthenticated, "无token认证信息")
}
// 调用 gRPC 处理函数处理请求
res, err := handler(ctx, req)
fmt.Println("请求已经完成") // 打印日志,表示请求处理完成
return res, err
}
// 创建 gRPC 服务器,并设置拦截器
opt := grpc.UnaryInterceptor(interceptor)
g := grpc.NewServer(opt)
// 在 gRPC 服务器上注册 Greeter 服务
proto.RegisterGreeterServer(g, &Server{})
// 监听指定地址和端口
lis, err := net.Listen("tcp", "0.0.0.0:50051")
if err != nil {
panic("failed to listen:" + err.Error())
}
// 启动 gRPC 服务器
err = g.Serve(lis)
if err != nil {
panic("failed to start grpc:" + err.Error())
}
}