gRPC 提供了 Interceptor 功能,包括客户端拦截器和服务端拦截器。可以在接收到请求或者发起请求之前优先对请求中的数据做一些处理后再转交给指定的服务处理并响应,很适合在这里处理验证、日志、请求耗时等流程
1、server
package main
import (
"context"
grpc "google.golang.org/grpc"
"log"
pb "mygrpc/proto"
"mygrpc/service"
"net"
"time"
)
func unaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
start := time.Now()
m, err := handler(ctx, req)
end := time.Now()
// 记录请求参数 耗时 错误信息等数据
log.Printf("RPC: %s,req:%v start time: %s, end time: %s, err: %v", info.FullMethod, req, start.Format(time.RFC3339), end.Format(time.RFC3339), err)
return m, err
}
func main() {
srv := grpc.NewServer(grpc.ChainUnaryInterceptor(unaryInterceptor))
pb.RegisterTestServiceServer(srv, &service.TestServiceImpl{})
listener, err := net.Listen("tcp", ":8002")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
err = srv.Serve(listener)
if err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
client
package main
import (
"context"
"google.golang.org/grpc"
"log"
pb "mygrpc/proto"
"time"
)
// unaryInterceptor 一个简单的 unary interceptor 示例。
func unaryInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
// pre-processing
start := time.Now()
err := invoker(ctx, method, req, reply, cc, opts...) // invoking RPC method
// post-processing
end := time.Now()
log.Printf("RPC: %s, req:%v start time: %s, end time: %s, err: %v", method, req, start.Format(time.RFC3339), end.Format(time.RFC3339), err)
return err
}
func main() {
conn, err := grpc.Dial("127.0.0.1:8002", grpc.WithInsecure(), grpc.WithUnaryInterceptor(unaryInterceptor))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
client := pb.NewTestServiceClient(conn)
resp, err := client.Query(context.Background(), &pb.Request{Name: "hello world!"})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Println(resp.Msg)
}