gRPC 的 go 拦截器

grpc interceptor with go

Interceptor

通过 ServerOption 给 grpc.Server 增加 interceptor 时有限制:Only one unary interceptor can be installed.。为了安装多个拦截器,我们需要一种可以将多个 interceptor 重新组合的方式。

目前常用的方式是:

        package : github.com/grpc-ecosystem/go-grpc-middleware:
            func ChainUnaryClient(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor
            func ChainUnaryServer(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor

使用示例见:enceladus.wgrpc.server

下边我们来看看它的实现:

        // Execution is done in left-to-right order, including passing of context.
        func ChainUnaryServer(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor {
            n := len(interceptors)

            if n > 1 {
                lastI := n - 1
                return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
                    var (
                        chainHandler grpc.UnaryHandler
                        curI         int
                    )

                    chainHandler = func(currentCtx context.Context, currentReq interface{}) (interface{}, error) {
                        if curI == lastI {
                            return handler(currentCtx, currentReq)
                        }
                        curI++
                        resp, err := interceptors[curI](currentCtx, currentReq, info, chainHandler)
                        curI--
                        return resp, err
                    }

                    return interceptors[0](ctx, req, info, chainHandler)
                }
            }

            if n == 1 {
                return interceptors[0]
            }

            // n == 0; Dummy interceptor maintained for backward compatibility to avoid returning nil.
            return func(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
                return handler(ctx, req)
            }
        }

其实,它的实现的本质就是:利用闭包来引用栈中数据达到延迟使用的效果

通过这种方式,我们就可以尽情的实现自己的各种 interceptor:UnaryServerInterceptorAccessLog、grpc_recovery.UnaryServerInterceptor(package: github.com/grpc-ecosystem/go-grpc-middleware/recovery) 等等。

grpc.stats

grpc.Server 可以通过安装 func WithStatsHandler(h stats.Handler) DialOption 生成的 ServerOption 实现 This package is for monitoring purpose only. All fields are read-only.

首先,我们看看 stats.Handler 的定义:

在 grpc 的方法调用过程中,RPCStats 在 stat.Begin、stat.ConnBegin、stat.ConnEnd、stat.End 几个状态之间转换。

其中 Handler.HandleConn 发生在 grpc 的方法调用之前,而 grpc方法调用后,会调用 Handler.HandleRPC (此时 RPCStats 为 stat.End)。由此,我们可以通过这种方式,达到对 grpc 方法 装饰的效果。如:github.com/openzipkin/zipkin-go 的实现,使用示例:

Appendix A

Last updated

Was this helpful?