Go 在自定义拦截器/中间件中访问gRPC请求对象
gRPC的Go库提供了创建自定义拦截器(即中间件函数)的接口,我正在尝试编写两个日志拦截器。第一个是一元服务器拦截器,我可以使用传递到拦截器函数中的对象轻松地记录请求参数Go 在自定义拦截器/中间件中访问gRPC请求对象,go,middleware,interceptor,grpc,Go,Middleware,Interceptor,Grpc,gRPC的Go库提供了创建自定义拦截器(即中间件函数)的接口,我正在尝试编写两个日志拦截器。第一个是一元服务器拦截器,我可以使用传递到拦截器函数中的对象轻松地记录请求参数 func loggingUnary(context context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { start := time.Now()
func loggingUnary(context context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
start := time.Now()
resp, err := handler(context, req)
printLogMessage(err, info.FullMethod, context, time.Since(start), req)
return resp, err
}
对于不能方便地将请求对象作为参数传递的流服务器拦截器,我如何做同样的事情?是否有其他方式访问请求
func loggingStream(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
start := time.Now()
err := handler(srv, stream)
printLogMessage(err, info.FullMethod, stream.Context(), time.Since(start), "")
return err
}
在创建流的请求的生命周期内,可以多次调用流处理程序,这就是为什么该请求不是处理程序(以及任何拦截器)参数的一部分。您可以在流上下文中放置请求(或者更好,是希望记录的数据的副本,而不是对请求本身的引用)(假设您控制着创建ServerStream对象的代码)。我宁愿在创建流时记录一次请求参数,而不是每次调用处理程序时记录一次(因此每个请求只记录一次)。这现在有点旧了,但将拦截扩展到流中的最简单方法是创建一个grpc.ServerStream包装器,然后将真正的ServerStream包装到拦截器中。这样,您的拦截代码就可以处理流中接收和发送的消息
// A wrapper for the real grpc.ServerStream
type LoggingServerStream struct {
inner grpc.ServerStream
}
func (l LoggingServerStream) SetHeader(m metadata.MD) error {
return l.SetHeader(m)
}
func (l LoggingServerStream) SendHeader(m metadata.MD) error {
return l.SendHeader(m)
}
func (l LoggingServerStream) SetTrailer(m metadata.MD) {
l.SetTrailer(m)
}
func (l LoggingServerStream) Context() context.Context {
return l.Context()
}
func (l LoggingServerStream) SendMsg(m interface{}) error {
fmt.Printf("Sending Message: type=%s\n", reflect.TypeOf(m).String())
return l.SendMsg(m)
}
func (l LoggingServerStream) RecvMsg(m interface{}) error {
fmt.Printf("Receiving Message: type=%s\n", reflect.TypeOf(m).String())
return l.RecvMsg(m)
}
拦截器:
func LoggingStreamInterceptor() grpc.StreamServerInterceptor {
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
return handler(srv, LoggingServerStream{inner:ss})
}
}
你需要保留的任何状态都可以放入包装中。我有点困惑。如果“控制创建ServerStream对象的代码”,您的意思是控制进行RPC调用的客户端,那么不,我没有控制权。然而,我控制着所有的服务器端代码。否则我如何访问请求?不,我指的是GO程序中创建ServerStream的部分(侦听您想要查看和记录的请求的部分)。这些代码应该是你的可执行文件的一部分(虽然不一定是你写的东西——它可能在别人开发的包中,我不知道)。上面的代码没有回答如何获得实际请求的问题。此代码截取服务器流而不是请求。我确认您的解决方案工作正常,但您犯了一个小错误,它“不是”返回l.RecvMsg(m)”,而是“l.inner.RecvMsg(m)”。RecvMsg中的消息实际上就是请求本身。