grpc拦截器

329 阅读1分钟

介绍

grpc服务端和客户端都提供了interceptor功能,功能类似middleware,很适合在这里处理验证,日志等流程。

类型

grpc中有两种拦截器

  • 普通方法(grpc.UnaryInterceptor)
  • 流方法 (grpc.StreamInterceptor)

grpc自带的拦截器

普通拦截器

源码

func WithUnaryInterceptor(f UnaryClientInterceptor) DialOption {
	return newFuncDialOption(func(o *dialOptions) {
		o.unaryInt = f
	})
}

由此可见,要实现拦截器需要实现UnaryClientInterceptor方法

type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error

流拦截器

func StreamInterceptor(i StreamServerInterceptor) ServerOption {
	return newFuncServerOption(func(o *serverOptions) {
		if o.streamInt != nil {
			panic("The stream server interceptor was already set and may not be reset.")
		}
		o.streamInt = i
	})
}

由此可见,要实现拦截器需要实现StreamServerInterceptor方法

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

问题

grpc本身只能设置一个拦截器,所以需要第三方工具来解决。

安装

go get github.com/grpc-ecosystem/go-grpc-middleware

简单案例

先写两个拦截器方法

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

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

server端集成拦截器

package main

import (
	"context"
	"fmt"
	"github.com/grpc-ecosystem/go-grpc-middleware"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"log"
	"net"
	"runtime/debug"
	pb "wsn-grpc/proto"
)

type SearchService struct {
}

func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) {
	fmt.Println("执行了")
	return &pb.SearchResponse{Response: r.GetRequest() + "server"}, nil
}

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

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



func main() {
	port := ":9091"
	opts := []grpc.ServerOption{
		grpc_middleware.WithUnaryServerChain(
			LoggintInterceptor,
			LoggintInterceptor2,
		),
	}
	server := grpc.NewServer(opts...)

	pb.RegisterSearchServiceServer(server, &SearchService{})
	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatal("net.listen err:%v", err)
	}
	server.Serve(lis)
}

测试

用client端调用search方法,server端执行结果如下:

image.png

可以发现:服务端search方法被调用一次,先执行拦截器LoggintInterceptor然后执行LoggintInterceptor2,最后LoggintInterceptor执行结束。