在 gRPC 调用过程中,我们可以拦截 RPC 的执行,在 RPC 服务执行前或执行后运行一些自定义逻辑。

这在某些场景下很有用,例如身份验证、日志等,我们可以在 RPC 服务执行前检查调用方的身份信息,若未通过验证,则拒绝执行,也可以在执行前后记录下详细的请求响应信息到日志。

这种拦截机制与 Gin 中的中间件技术类似,在 gRPC 中被称为 拦截器,它是 gRPC 核心扩展机制之一。

拦截器不止可以作用在服务端上,客户端同样可以拦截,在请求发出之前和收到响应之后执行一些自定义逻辑,根据拦截的 RPC 类型,可分为 一元拦截器 和 流拦截器。

gRPC的拦截分类

  • 按功能来分

    1. 一元拦截器 UnaryInterceptor
    2. 流式拦截器 StreamInterceptor
  • 按端来分

    1. 客户端拦截器 ClientInterceptor
    2. 服务端拦截器 ServerIntercepto

      服务端拦截器

      在 gRPC 服务端,可以插入一个或多个拦截器,收到的请求按注册顺序通过各个拦截器,返回响应时则倒序通过:

gRPC 拦截器 - 图1

一元拦截器

通过以下步骤实现一元拦截器:

  • 定义一元拦截器方法

    1. // 函数名无特殊要求,参数需一致
    2. // req包含请求的所有信息,info包含一元RPC服务的所有信息
    3. func orderUnaryServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
    4. handler grpc.UnaryHandler) (interface{}, error) {
    5. // 前置处理逻辑
    6. log.Printf("[unary interceptor request] %s", info.FullMethod)
    7. // 完成RPC服务的正常执行
    8. m, err := handler(ctx, req)
    9. // 后置处理逻辑
    10. log.Printf("[unary interceptor resonse] %s", m)
    11. // 返回响应
    12. return m, err
    13. }
  • 注册定义的一元拦截器 ```go

func main() { … // 创建gRPC服务器实例的时候注册拦截器 // NewServer 可传入多个拦截器 s := grpc.NewServer(grpc.UnaryInterceptor(orderUnaryServerInterceptor)) … }

  1. <a name="wl9F7"></a>
  2. ### 流拦截器
  3. 流拦截器包括前置处理阶段和流操作阶段,前置处理阶段可以在流 RPC 进入具体服务实现之前进行拦截,而在流操作阶段,可以对流中的每一条消息进行拦截,通过以下步骤实现流拦截器:
  4. - 自定义一个嵌入grpc.ServerStream的包装器
  5. ```go
  6. type wrappedStream struct {
  7. grpc.ServerStream
  8. }
  • 实现包装器的 RecvMsg 和 SendMsg 方法 ```go // 自定义RecvMsg和SendMsg方法实现对每一个流消息的拦截 func (w *wrappedStream) RecvMsg(m interface{}) error { log.Printf(“[stream interceptor recv] type: %T”, m) return w.ServerStream.RecvMsg(m) }

func (w *wrappedStream) SendMsg(m interface{}) error { log.Printf(“[stream interceptor send] %s”, m) return w.ServerStream.SendMsg(m) } ```