GRPC 自定义一元(一次性)拦截器
GRPC(Google Remote Procedure Call)
是一种高性能、跨平台的远程过程调用框架,用于构建分布式系统。GRPC拦截器是在GRPC框架中用于拦截和处理请求和响应的组件。
拦截器提供了一种可扩展的方式来添加逻辑,例如身份验证、授权、日志记录、性能监控等,以在请求到达目标服务之前或响应返回给客户端之前对其进行处理。
在GRPC中,拦截器由服务器端和客户端两个方面组成:
- 服务器端拦截器:服务器端拦截器用于拦截和处理从客户端发送到服务器的请求。它们可以在请求到达服务器之前或响应返回给客户端之前对其进行处理。服务器端拦截器可以实现各种功能,例如身份验证、授权、错误处理等。通过实现
ServerInterceptor
接口,并注册到服务器的拦截器链中,可以自定义服务器端拦截器。 - 客户端拦截器:客户端拦截器用于拦截和处理从客户端发送到服务器的请求和从服务器返回的响应。它们可以在请求发送到服务器之前或响应返回给客户端之前对其进行处理。客户端拦截器可以实现各种功能,例如身份验证、添加标头、错误处理等。通过实现
ClientInterceptor
接口,并在创建GRPC客户端时将其注册到客户端的拦截器链中,可以自定义客户端拦截器。
无论是服务器端拦截器还是客户端拦截器,它们都可以访问和修改请求和响应的元数据、标头和有效载荷。这使得它们非常有用,可以在不影响业务逻辑的情况下添加通用的横切关注点(cross-cutting concerns)。
使用GRPC拦截器,你可以轻松地实现各种功能,以满足你的应用程序的需求,并使其更具可维护性和扩展性。
自定义一元(一次性)服务端拦截器
首先针对一次性的服务端请求定义一个RPC服务的proto
syntax = "proto3"; // 版本
package hello; //包声明
option go_package = "./user";
message AimUser {
string name = 1;
}
service User {
rpc SayHi(AimUser) returns(AimUser); //定义服务的某一个方法,接受的参数,和返回值
}
实现服务端方法
type UserService struct {
user.UnimplementedUserServer
}
func (u UserService) SayHi(ctx context.Context, aimUser *user.AimUser) (*user.AimUser, error) {
name := aimUser.GetName()
return &user.AimUser{Name: "HI" + name}, nil
}
自定义拦截器 -> 实现 ServerInterceptor
接口
// CustomInterceptor 自定义拦截器结构体
type CustomInterceptor struct {
}
// UnaryServerInterceptor 实现拦截器的 UnaryServerInterceptor 方法里面就是具体的拦截器内部要做的拦截逻辑!
func (i *CustomInterceptor) UnaryServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
// 在处理请求之前执行的逻辑
log.Println("CustomInterceptor: Before handling the request")
// 调用下一个拦截器或服务处理程序
resp, err := handler(ctx, req)
// 在处理请求后执行的逻辑
log.Println("CustomInterceptor: After handling the request")
return resp, err
}
grpc.UnaryInterceptor
传入拦截器方法->转为 serverOption
-> 注册到server
func main() {
ci := new(CustomInterceptor)
// 创建一个新的GRPC服务器
i := grpc.UnaryInterceptor(ci.UnaryServerInterceptor)
// 注册自定义拦截器
server := grpc.NewServer(
i,
)
// 注册你的GRPC服务
user.RegisterUserServer(server, new(UserService))
// 启动监听指定端口
listener, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
// 启动GRPC服务器
if err := server.Serve(listener); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}
客户端代码实现!
func main() {
// 建立 grpc与服务端套接字通信
conn, err := grpc.Dial(":50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return
}
//defer 关闭链接
defer func(conn *grpc.ClientConn) {
err := conn.Close()
if err != nil {
log.Fatalf("关闭 con 套接字失败 err:%s", err.Error())
}
}(conn)
// 创建一个客户端实例
client := user.NewUserClient(conn)
//调用客户端封装好的 访问服务方法
hi, err := client.SayHi(context.Background(), &user.AimUser{
Name: "村望老弟",
})
if err != nil {
return
}
fmt.Println(hi)
}
可以看到自定义服务端拦截器OK了!!
自定义一元(一次性的客户端拦截器)
通过实现
ClientInterceptor
接口,来实现我们自定义客户端的拦截器!
自定义拦截器结构体 -> 实现拦截器的UnaryClientInterceptor
方法
// 自定义拦截器结构体
type CustomInterceptor struct{}
// UnaryClientInterceptor 实现拦截器的UnaryClientInterceptor方法
func (i *CustomInterceptor) UnaryClientInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
// 在发送请求之前执行的逻辑
log.Println("CustomInterceptor: Before sending the request")
// 获取元数据,并添加自定义标头
md := metadata.Pairs()
md.Set("custom-header", "======custom-header======")
// NewOutgoingContext 拼装携带 metadata的context!
ctx = metadata.NewOutgoingContext(ctx, md)
// 调用下一个拦截器或调用RPC的方法!
err := invoker(ctx, method, req, reply, cc, opts...)
// 这里会输出在这里执行的方法!
fmt.Println("invoke", method)
// 在接收响应后执行的逻辑
log.Println("CustomInterceptor: After receiving the response")
return err
}
grpc.WithUnaryInterceptor
传入拦截器方法 ->转为 DialOption
-> Dia传入自定义拦截器DialOption
这里对服务端的拦截器做一点改造!因为这里客户端添加了一些 ·metadata· 服务端我们那里接收一下看看效果!
// UnaryServerInterceptor 实现拦截器的UnaryServerInterceptor方法里面就是具体的拦截器内部要做的拦截逻辑!
func (i *CustomInterceptor) UnaryServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
// 在处理请求之前执行的逻辑
log.Println("CustomInterceptor: Before handling the request")
// FromIncomingContext 获取metadata!
if outgoingContext, b := metadata.FromIncomingContext(ctx); b {
fmt.Println(outgoingContext.Get("custom-header"))
} else {
fmt.Println("no custom-header!")
}
// 调用下一个拦截器或服务处理程序
resp, err := handler(ctx, req)
if err != nil {
return nil, err
}
fmt.Println(resp)
// 在处理请求后执行的逻辑
log.Println("CustomInterceptor: After handling the request")
return resp, err
}
评论区