gRPC实践--拦截器

前提阅读:

本文的代码,可能在以上代码的基础上进行了更改。


前言

什么是拦截器?

拦截器(Interceptor),主要完成请求参数的解析、将页面表单参数赋给值栈中相应属性、执行功能检验、程序异常调试等工作。

说人话,就是,我需要在调用 gRPC 方法之前(或之后),对某些参数(如,日志、异常,甚至是token)做一些处理。

实现这类功能的活,就叫“拦截器”。

gRPC中的拦截器

gRPC中,分别对普通方法和流方法提供了截取器的支持,也就是:

  • 一元拦截器:grpc.UnaryInterceptor
  • 流拦截器:grpc.StreamInterceptor

注意:但是,gRPC框架中只能为每个服务设置一个截取器,因此所有的截取工作只能在一个函数中完成。要想为每个服务设置多个不同的拦截器也可以,需要拉取一个开源的依赖包:go-grpc-middleware。在开源的grpc-ecosystem项目中的go-grpc-middleware包已经基于gRPC对截取器实现了链式截取器的支持。

解析

本文重点来看一元拦截器,其实流拦截器也是类似的。grpc.UnaryInterceptor中的结构如下所示

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

其中,UnaryServerInterceptor的接口类似如下所示:

在这里插入图片描述

要实现普通方法的截取器(一元拦截器),就需要为 grpc.UnaryInterceptor 的参数,即UnaryServerInterceptor 实现一个函数。

例子:

func myLogFilter(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
	log.Printf("gRPC method: %s, %v", info.FullMethod, req)
	resp, err = handler(ctx, req)
	log.Println("resp is ", resp)
	return resp, err
}

以上,简单实现了一个拦截器。

解释:

  • ctx context.Context:请求上下文
  • req interface{}:RPC 方法的请求参数
  • info *UnaryServerInfo:RPC 方法的所有信息,表示当前是对应的那个gRPC方法
  • handler UnaryHandler:RPC 方法本身,表示对应当前的gRPC方法函数

-----------参考:带入gRPC:Unary and Stream interceptor

使用:

要使用filter截取器函数,只需要在启动gRPC服务时作为参数输入即可:

server := grpc.NewServer(grpc.UnaryInterceptor(myLogFilter))

实现

代码修改

为了代码不用太多的改动,本文在server.go端进行了一定代码修改:

package main

......

// 日志和异常拦截
func myLogFilter(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
	// 输出日志
	log.Printf("gRPC method: %s, %v", info.FullMethod, req)
	resp, err = handler(ctx, req)
	// 拦截异常
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("panic: %v", r)
		}
	}()
	log.Println("resp is ", resp)
	return resp, err
}

func main() {
	// 引入证书
	certificate, err := tls.LoadX509KeyPair("server.pem", "server.key")
	......
	creds := credentials.NewTLS(&tls.Config{
		Certificates: []tls.Certificate{certificate},
		ClientAuth:   tls.RequireAndVerifyClientCert, // NOTE: this is optional!
		ClientCAs:    certPool,
	})
	// 初始化一个gRPC的结构体对象
    // 开启证书,并传入拦截器
	s := grpc.NewServer(grpc.Creds(creds), grpc.UnaryInterceptor(myLogFilter))
	// 注册服务
	pb.RegisterGreeterServer(s, &server{})
	......
}

实验

服务端启动:

go run server.go

客户端启动:

go run client.go

结果:

在这里插入图片描述

多拦截器

注意:但是,gRPC框架中只能为每个服务设置一个截取器,因此所有的截取工作只能在一个函数中完成。要想为每个服务设置多个不同的拦截器也可以,需要拉取一个开源的依赖包:go-grpc-middleware。在开源的grpc-ecosystem项目中的go-grpc-middleware包已经基于gRPC对截取器实现了链式截取器的支持。

以下是go-grpc-middleware包中链式截取器的简单用法

import "github.com/grpc-ecosystem/go-grpc-middleware"

myServer := grpc.NewServer(
    grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
        filter1, filter2, ...
    )),
    grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
        filter1, filter2, ...
    )),
)

----------参考:Go语言高级编程 · 4.5 gRPC进阶

总结

本文主要介绍了gRPC中的拦截器的概念、作用和简单的用法。

具体的深入实践还需要再实际生产过程中进行摸索。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值