介绍
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端执行结果如下:
可以发现:服务端search方法被调用一次,先执行拦截器LoggintInterceptor然后执行LoggintInterceptor2,最后LoggintInterceptor执行结束。