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 }