github.com/thanos-io/thanos@v0.32.5/pkg/server/grpc/grpc.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package grpc
     5  
     6  import (
     7  	"context"
     8  	"math"
     9  	"net"
    10  	"runtime/debug"
    11  
    12  	"github.com/go-kit/log"
    13  	"github.com/go-kit/log/level"
    14  	"github.com/grpc-ecosystem/go-grpc-middleware/providers/kit/v2"
    15  	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware/v2"
    16  	grpc_logging "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
    17  	grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
    18  	"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/tags"
    19  	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
    20  	"github.com/opentracing/opentracing-go"
    21  	"github.com/pkg/errors"
    22  	"github.com/prometheus/client_golang/prometheus"
    23  	"github.com/prometheus/client_golang/prometheus/promauto"
    24  	"google.golang.org/grpc"
    25  	"google.golang.org/grpc/codes"
    26  	"google.golang.org/grpc/credentials"
    27  	grpc_health "google.golang.org/grpc/health/grpc_health_v1"
    28  	"google.golang.org/grpc/keepalive"
    29  	"google.golang.org/grpc/reflection"
    30  	"google.golang.org/grpc/status"
    31  
    32  	"github.com/thanos-io/thanos/pkg/component"
    33  	"github.com/thanos-io/thanos/pkg/prober"
    34  	"github.com/thanos-io/thanos/pkg/tracing"
    35  )
    36  
    37  // A Server defines parameters to serve RPC requests, a wrapper around grpc.Server.
    38  type Server struct {
    39  	logger log.Logger
    40  	comp   component.Component
    41  
    42  	srv      *grpc.Server
    43  	listener net.Listener
    44  
    45  	opts options
    46  }
    47  
    48  // New creates a new gRPC Store API.
    49  // If rulesSrv is not nil, it also registers Rules API to the returned server.
    50  func New(logger log.Logger, reg prometheus.Registerer, tracer opentracing.Tracer, logOpts []grpc_logging.Option, tagsOpts []tags.Option, comp component.Component, probe *prober.GRPCProbe, opts ...Option) *Server {
    51  	logger = log.With(logger, "service", "gRPC/server", "component", comp.String())
    52  	options := options{
    53  		network: "tcp",
    54  	}
    55  	for _, o := range opts {
    56  		o.apply(&options)
    57  	}
    58  
    59  	met := grpc_prometheus.NewServerMetrics()
    60  	met.EnableHandlingTimeHistogram(
    61  		grpc_prometheus.WithHistogramBuckets([]float64{0.001, 0.01, 0.1, 0.3, 0.6, 1, 3, 6, 9, 20, 30, 60, 90, 120}),
    62  	)
    63  	panicsTotal := promauto.With(reg).NewCounter(prometheus.CounterOpts{
    64  		Name: "grpc_req_panics_recovered_total",
    65  		Help: "Total number of gRPC requests recovered from internal panic.",
    66  	})
    67  
    68  	grpcPanicRecoveryHandler := func(p interface{}) (err error) {
    69  		panicsTotal.Inc()
    70  		level.Error(logger).Log("msg", "recovered from panic", "panic", p, "stack", debug.Stack())
    71  		return status.Errorf(codes.Internal, "%s", p)
    72  	}
    73  
    74  	options.grpcOpts = append(options.grpcOpts, []grpc.ServerOption{
    75  		// NOTE: It is recommended for gRPC messages to not go over 1MB, yet it is typical for remote write requests and store API responses to go over 4MB.
    76  		// Remove limits and allow users to use histogram message sizes to detect those situations.
    77  		// TODO(bwplotka): https://github.com/grpc-ecosystem/go-grpc-middleware/issues/462
    78  		grpc.MaxSendMsgSize(math.MaxInt32),
    79  		grpc.MaxRecvMsgSize(math.MaxInt32),
    80  		grpc_middleware.WithUnaryServerChain(
    81  			grpc_recovery.UnaryServerInterceptor(grpc_recovery.WithRecoveryHandler(grpcPanicRecoveryHandler)),
    82  			met.UnaryServerInterceptor(),
    83  			tags.UnaryServerInterceptor(tagsOpts...),
    84  			tracing.UnaryServerInterceptor(tracer),
    85  			grpc_logging.UnaryServerInterceptor(kit.InterceptorLogger(logger), logOpts...),
    86  		),
    87  		grpc_middleware.WithStreamServerChain(
    88  			grpc_recovery.StreamServerInterceptor(grpc_recovery.WithRecoveryHandler(grpcPanicRecoveryHandler)),
    89  			met.StreamServerInterceptor(),
    90  			tags.StreamServerInterceptor(tagsOpts...),
    91  			tracing.StreamServerInterceptor(tracer),
    92  			grpc_logging.StreamServerInterceptor(kit.InterceptorLogger(logger), logOpts...),
    93  		),
    94  	}...)
    95  
    96  	if options.tlsConfig != nil {
    97  		options.grpcOpts = append(options.grpcOpts, grpc.Creds(credentials.NewTLS(options.tlsConfig)))
    98  	}
    99  	if options.maxConnAge > 0 {
   100  		options.grpcOpts = append(options.grpcOpts, grpc.KeepaliveParams(keepalive.ServerParameters{MaxConnectionAge: options.maxConnAge}))
   101  	}
   102  	s := grpc.NewServer(options.grpcOpts...)
   103  
   104  	// Register all configured servers.
   105  	for _, f := range options.registerServerFuncs {
   106  		f(s)
   107  	}
   108  
   109  	met.InitializeMetrics(s)
   110  	reg.MustRegister(met)
   111  
   112  	grpc_health.RegisterHealthServer(s, probe.HealthServer())
   113  	reflection.Register(s)
   114  
   115  	return &Server{
   116  		logger: logger,
   117  		comp:   comp,
   118  		srv:    s,
   119  		opts:   options,
   120  	}
   121  }
   122  
   123  // ListenAndServe listens on the TCP network address and handles requests on incoming connections.
   124  func (s *Server) ListenAndServe() error {
   125  	l, err := net.Listen(s.opts.network, s.opts.listen)
   126  	if err != nil {
   127  		return errors.Wrapf(err, "listen gRPC on address %s", s.opts.listen)
   128  	}
   129  	s.listener = l
   130  
   131  	level.Info(s.logger).Log("msg", "listening for serving gRPC", "address", s.opts.listen)
   132  	return errors.Wrap(s.srv.Serve(s.listener), "serve gRPC")
   133  }
   134  
   135  // Shutdown gracefully shuts down the server by waiting,
   136  // for specified amount of time (by gracePeriod) for connections to return to idle and then shut down.
   137  func (s *Server) Shutdown(err error) {
   138  	level.Info(s.logger).Log("msg", "internal server is shutting down", "err", err)
   139  
   140  	if s.opts.gracePeriod == 0 {
   141  		s.srv.Stop()
   142  		level.Info(s.logger).Log("msg", "internal server is shutdown", "err", err)
   143  		return
   144  	}
   145  
   146  	ctx, cancel := context.WithTimeout(context.Background(), s.opts.gracePeriod)
   147  	defer cancel()
   148  
   149  	stopped := make(chan struct{})
   150  	go func() {
   151  		level.Info(s.logger).Log("msg", "gracefully stopping internal server")
   152  		s.srv.GracefulStop() // Also closes s.listener.
   153  		close(stopped)
   154  	}()
   155  
   156  	select {
   157  	case <-ctx.Done():
   158  		level.Info(s.logger).Log("msg", "grace period exceeded enforcing shutdown")
   159  		s.srv.Stop()
   160  		return
   161  	case <-stopped:
   162  		cancel()
   163  	}
   164  	level.Info(s.logger).Log("msg", "internal server is shutdown gracefully", "err", err)
   165  }