gRPC 之 Interceptors

gRPC 环境安装 Mac

由于仓库已经转到 github,命令 go get -u google.golang.org/grpc 已经不能正常工作。

$ brew install libtool
$ brew install automake

# install proto3
$ mkdir tmp
$ cd tmp
$ git clone https://github.com/google/protobuf
$ cd protobuf
$ ./autogen.sh
$ ./configure
$ make
$ make check
$ sudo make install

# install go-grpc, 由于仓库已经转到 github,因此下面命令不能正常工作
# $ go get -u google.golang.org/grpc
$ git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
$ git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net
$ git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text
$ git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto

# $ git clone https://github.com/golang/crypto.git $GOPATH/src/golang.org/x/crypto
# $ git clone https://github.com/golang/sys.git $GOPATH/src/golang.org/x/sys

$ cd $GOPATH/src/ && go install google.golang.org/grpc

$ go get -u github.com/golang/protobuf/{proto,protoc-gen-go}

# go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
# go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
$ go get -u github.com/golang/protobuf/protoc-gen-go

gPRC Interceptors

当前实现的 Interceptors 包括 Auth/Log/Monitor/Trace 等。(auth, logging , trace,message, validation, retries or monitoring),大本营: https://github.com/grpc-ecosystem/go-grpc-middleware

类型两种:

  • UnaryInterceptor
  • StreamInterceptor

常见拦截器列表如下:

  1. Go gRPC Middleware: 提供了拦截器的interceptor链式的功能,可以将多个拦截器组合成一个拦截器链,当然它还提供了其它的功能,所以以gRPC中间件命名。
  2. grpc-multi-interceptor: 是另一个interceptor链式功能的库,也可以将单向的或者流式的拦截器组合。
  3. grpc_auth: 身份验证拦截器
  4. grpc_ctxtags: 为上下文增加Tag map对象
  5. grpc_zap: 支持zap日志框架
  6. grpc_logrus: 支持logrus日志框架
  7. grpc_prometheus: 支持 prometheus
  8. otgrpc: 支持opentracing/zipkin
  9. grpc_opentracing:支持opentracing/zipkin
  10. grpc_retry: 为客户端增加重试的功能
  11. grpc_validator: 为服务器端增加校验的功能
  12. xrequestid: 将request id 设置到context中
  13. go-grpc-interceptor: 解析Accept-Language并设置到context
  14. requestdump: 输出request/response

Prometheus 样例如下:

添加了 Zipkin 与 Logger 的代码样例:

client

package main

import (
    "log"
    "os"
    "time"

    "golang.org/x/net/context"
    "google.golang.org/grpc"
    pb "google.golang.org/grpc/examples/helloworld/helloworld"

)

const (
    address     = "localhost:50051"
    defaultName = "world"
)

func main() {

    // Set up a connection to the server.
    conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBalancerName())
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    c := pb.NewGreeterClient(conn)

    // Contact the server and print out its response.
    name := defaultName
    if len(os.Args) > 1 {
        name = os.Args[1]
    }

    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()
    r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
    if err != nil {
        log.Fatalf("could not greet: %v", err)
    }
    log.Printf("Greeting: %s", r.Message)
}

server.go

package main

import (
    "log"
    "net"

    "golang.org/x/net/context"
    "google.golang.org/grpc"
    pb "google.golang.org/grpc/examples/helloworld/helloworld"
    "google.golang.org/grpc/reflection"
    "github.com/sirupsen/logrus"
    "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
    "github.com/grpc-ecosystem/go-grpc-middleware"
    "github.com/grpc-ecosystem/go-grpc-middleware/tags"

    "github.com/opentracing/opentracing-go"
    zipkin "github.com/openzipkin-contrib/zipkin-go-opentracing"
    "fmt"
    "os"
    "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing"
)

const (
    port = ":50051"
)

// server is used to implement helloworld.GreeterServer.
type server struct{}

// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

var (
    logrusLogger *logrus.Logger
    customFunc   grpc_logrus.CodeToLevel
)

func init(){
    // docker run -d -p 9411:9411 -p 9410:9410 openzipkin/zipkin

    // create collector.
    collector, err := zipkin.NewHTTPCollector("http://localhost:9411/api/v1/spans")
    if err != nil {
        fmt.Printf("unable to create Zipkin HTTP collector: %+v\n", err)
        os.Exit(-1)
    }

    // create recorder.
    recorder := zipkin.NewRecorder(collector, false, "127.0.0.1:50051", "greeter_server")

    // create tracer.
    tracer, err := zipkin.NewTracer(
        recorder,
        zipkin.ClientServerSameSpan(true),
        zipkin.TraceID128Bit(true),
    )
    if err != nil {
        fmt.Printf("unable to create Zipkin tracer: %+v\n", err)
        os.Exit(-1)
    }

    // explicitly set our tracer to be the default tracer.
    opentracing.InitGlobalTracer(tracer)
}

func main() {

    // Logrus entry is used, allowing pre-definition of certain fields by the user.
    logrusLogger = logrus.New()
    logrusLogger.SetFormatter(&logrus.JSONFormatter{})
    logrusEntry := logrus.NewEntry(logrusLogger)
    // Shared options for the logger, with a custom gRPC code to log level function.

    customFunc = grpc_logrus.DefaultCodeToLevel
    opts := []grpc_logrus.Option{
        grpc_logrus.WithLevels(customFunc),
    }

    // Shared options for the logger, with a custom duration to log field function.
    //opts := []grpc_logrus.Option{
    //  grpc_logrus.WithDurationField(func(duration time.Duration) (key string, value interface{}) {
    //      return "grpc.time_ns", duration.Nanoseconds()
    //  }),
    //}

    // Make sure that log statements internal to gRPC library are logged using the logrus Logger as well.
    grpc_logrus.ReplaceGrpcLogger(logrusEntry)

    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }

    // Create a server, make sure we put the grpc_ctxtags context before everything else.
    s := grpc.NewServer(
        grpc_middleware.WithUnaryServerChain(
            grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
            grpc_logrus.UnaryServerInterceptor(logrusEntry, opts...),
            grpc_opentracing.UnaryServerInterceptor(),
        ),
        grpc_middleware.WithStreamServerChain(
            grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
            grpc_logrus.StreamServerInterceptor(logrusEntry, opts...),
            grpc_opentracing.StreamServerInterceptor(),

        ),
    )

    // s := grpc.NewServer()
    pb.RegisterGreeterServer(s, &server{})
    // Register reflection service on gRPC server.
    reflection.Register(s)
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

server 端的产奖配置可以如下:

import "github.com/grpc-ecosystem/go-grpc-middleware"

myServer := grpc.NewServer(
    grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
        grpc_ctxtags.StreamServerInterceptor(),
        grpc_opentracing.StreamServerInterceptor(),
        grpc_prometheus.StreamServerInterceptor,
        grpc_zap.StreamServerInterceptor(zapLogger),
        grpc_auth.StreamServerInterceptor(myAuthFunction),
        grpc_recovery.StreamServerInterceptor(),
    )),
    grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
        grpc_ctxtags.UnaryServerInterceptor(),
        grpc_opentracing.UnaryServerInterceptor(),
        grpc_prometheus.UnaryServerInterceptor,
        grpc_zap.UnaryServerInterceptor(zapLogger),
        grpc_auth.UnaryServerInterceptor(myAuthFunction),
        grpc_recovery.UnaryServerInterceptor(),
    )),
)

分类如下:

Auth

  • grpc_auth - a customizable (via AuthFunc) piece of auth middleware

Logging

  • grpc_ctxtags - a library that adds a Tag map to context, with data populated from request body
  • grpc_zap - integration of zap logging library into gRPC handlers.
  • grpc_logrus - integration of logrus logging library into gRPC handlers.

Monitoring

Client

  • grpc_retry - a generic gRPC response code retry mechanism, client-side middleware

Server

参考

  1. Golang gRPC实践 连载五 拦截器 Interceptor
  2. Writing gRPC Interceptors in Go

发表评论

电子邮件地址不会被公开。 必填项已用*标注