zoukankan      html  css  js  c++  java
  • 跟我一起学 Go 系列:gRPC 拦截器

    Go gRPC 学习系列:

    1. 跟我一起学Go系列:gRPC 入门必备

    第一篇内容我们已经基本了解到 gRPC 如何使用 、对应的三种流模式。现在已经可以让服务端和客户端互相发送消息。本篇仍然讲解功能性的使用说明:如何使用拦截器。使用过 Java 的同学知道 Spring 或者 Dubbo,这两个框架都提供了拦截器的支持,拦截器的作用无需多言,鉴权,Tracing,数据统计等等。

    在 gRPC 中拦截器的实现会稍微有所不同,原因在于 gRPC 多了一种流式数据传输模式。所以这种拦截器的处理也变得相对复杂。

    拦截器类型

    1. UnaryServerInterceptor 服务端拦截,在服务端接收请求的时候进行拦截。
    2. UnaryClientInterceptor 这是一个客户端上的拦截器,在客户端真正发起调用之前,进行拦截。
    3. StreamClientInterceptor 在流式客户端调用时,通过拦截 clientstream 的创建,返回一个自定义的 clientstream, 可以做一些额外的操作。
    4. StreamServerInterceptor 在服务端接收到流式请求的时候进行拦截。

    拦截器使用

    普通拦截器

    在 gRPC 中拦截器被定义成一个变量:

    type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)
    

    参数含义如下:

    • ctx context.Context:请求上下文
    • req interface{}:RPC 方法的请求参数
    • info *UnaryServerInfo:RPC 方法的所有信息
    • handler UnaryHandler:RPC 方法真正执行的逻辑

    它本质是一个方法,拦截器的应用是在服务端启动的时候要注册上去,从 grpc.NewServer(opts...) 这里开始,这里需要一个 ServerOption 对象:

    //注册拦截器 创建gRPC服务器
    s := grpc.NewServer(grpc.UnaryInterceptor(LoggingInterceptor))  
    

    gRPC 在 v1.28.0 版本增加了多 interceptor 支持,可以在不借助第三方库(go-grpc-middleware)的情况下添加多个 interceptor。看一下 grpc.UnaryInterceptor() 方法的定义:

    func UnaryInterceptor(i UnaryServerInterceptor) ServerOption {
    	return newFuncServerOption(func(o *serverOptions) {
    		if o.unaryInt != nil {
    			panic("The unary server interceptor was already set and may not be reset.")
    		}
    		o.unaryInt = i
    	})
    }
    

    参数为一个 UnaryServerInterceptor ,即传入 UnaryServerInterceptor 类型的方法即可,所以自定义一个拦截器就变得很简单,只需要定义一个 UnaryServerInterceptor 类型的方法。比如我们实现一个打印日志的拦截器:

    //拦截器 - 打印日志
    func LoggingInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
    												handler grpc.UnaryHandler) (interface{}, error) {
    	fmt.Printf("gRPC method: %s, %v", info.FullMethod, req)
    	resp, err := handler(ctx, req)
    	fmt.Printf("gRPC method: %s, %v", info.FullMethod, resp)
    	return resp, err
    }
    

    可以看到只需要按照 UnaryServerInterceptor 方法的参数去构造即可。然后就是应用,在 server 注册的时候:

    grpc.NewServer(grpc.UnaryInterceptor(LoggingInterceptor))  // 创建gRPC服务器
    ......
    ......
    ......
    

    将拦截器注册上就行,是不是很简单。

    流拦截器

    流拦截器过程和一元拦截器有所不同,同样可以分为3个阶段:

    • 预处理(pre-processing)
    • 调用RPC方法(invoking RPC method)
    • 后处理(post-processing)

    预处理阶段的拦截只是在流式请求第一次 发起的时候进行拦截,后面的流式请求不会再进入到处理逻辑。

    后面两种情况对应着 Streamer api 提供的两个扩展方法来进行,分别是 SendMsg 和 RecvMsg 方法。

    正常情况下实现一个流式拦截器与普通拦截器一样,实现这个已经定义好的拦截器方法即可:

    type StreamServerInterceptor func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error
    

    如果是想在发消息之前和之进行处理, 则实现 SendMsg 和 RecvMsg:

    type wrappedStream struct {
    	grpc.ServerStream
    }
    
    func newWrappedStream(s grpc.ServerStream) grpc.ServerStream {
    	return &wrappedStream{s}
    }
    
    func (w *wrappedStream) RecvMsg(m interface{}) error {
    	fmt.Printf("Receive a message (Type: %T) at %s", m, time.Now().Format(time.RFC3339))
    	return w.ServerStream.RecvMsg(m)
    }
    
    func (w *wrappedStream) SendMsg(m interface{}) error {
    	fmt.Printf("Send a message (Type: %T) at %v", m, time.Now().Format(time.RFC3339))
    	return w.ServerStream.SendMsg(m)
    }
    

    首先我们自己包装一个 grpc.ServerStream ,然后去实现它的 SendMsg 和 RecvMsg 方法。然后就是将这个 ServerStream 应用到拦截器中去:

    //发消息前后流式调用拦截器
    func SendMsgCheckStreamServerInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo,
    	handler grpc.StreamHandler) error {
    	fmt.Printf("gRPC method: %s,", info.FullMethod)
    	err := handler(srv, newWrappedStream(ss))
    	fmt.Printf("gRPC method: %s", info.FullMethod)
    	return err
    }
    

    这里注意到参数 ss 使用我们自己定义的 ServerStream 包装一下。

    相关测试代码都在这里,点击查看

  • 相关阅读:
    C++函数四( 具有默认参数值的函数)
    C++函数三(内联函数和函数重载)
    C++函数二(函数的嵌套调用和递归调用)
    C++关于函数声明定义的位置
    C++函数一(标准库函数,自定义函数)
    C++扑克牌发牌游戏程序(包括C++随机数的解释)
    C++数组二(字符数组)
    C++数组一
    C++程序流程结构
    C++运算符与表达式
  • 原文地址:https://www.cnblogs.com/rickiyang/p/14975552.html
Copyright © 2011-2022 走看看