github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/querier/worker_service.go (about)

     1  package querier
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  
     7  	"github.com/go-kit/log/level"
     8  	"github.com/gorilla/mux"
     9  	"github.com/grafana/dskit/ring"
    10  	"github.com/grafana/dskit/services"
    11  	"github.com/opentracing-contrib/go-stdlib/nethttp"
    12  	"github.com/opentracing/opentracing-go"
    13  	"github.com/prometheus/client_golang/prometheus"
    14  	httpgrpc_server "github.com/weaveworks/common/httpgrpc/server"
    15  	"github.com/weaveworks/common/middleware"
    16  
    17  	querier_worker "github.com/grafana/loki/pkg/querier/worker"
    18  	"github.com/grafana/loki/pkg/util/httpreq"
    19  	util_log "github.com/grafana/loki/pkg/util/log"
    20  	serverutil "github.com/grafana/loki/pkg/util/server"
    21  )
    22  
    23  type WorkerServiceConfig struct {
    24  	AllEnabled            bool
    25  	ReadEnabled           bool
    26  	GrpcListenPort        int
    27  	QuerierMaxConcurrent  int
    28  	QuerierWorkerConfig   *querier_worker.Config
    29  	QueryFrontendEnabled  bool
    30  	QuerySchedulerEnabled bool
    31  	SchedulerRing         ring.ReadRing
    32  }
    33  
    34  // InitWorkerService takes a config object, a map of routes to handlers, an external http router and external
    35  // http handler, and an auth middleware wrapper. This function creates an internal HTTP router that responds to all
    36  // the provided query routes/handlers. This router can either be registered with the external Loki HTTP server, or
    37  // be used internally by a querier worker so that it does not conflict with the routes registered by the Query Frontend module.
    38  //
    39  // 1. Query-Frontend Enabled: If Loki has an All or QueryFrontend target, the internal
    40  //    HTTP router is wrapped with Tenant ID parsing middleware and passed to the frontend
    41  //    worker.
    42  //
    43  // 2. Querier Standalone: The querier will register the internal HTTP router with the external
    44  //    HTTP router for the Prometheus API routes. Then the external HTTP server will be passed
    45  //    as a http.Handler to the frontend worker.
    46  //
    47  func InitWorkerService(
    48  	cfg WorkerServiceConfig,
    49  	reg prometheus.Registerer,
    50  	queryRoutesToHandlers map[string]http.Handler,
    51  	alwaysExternalRoutesToHandlers map[string]http.Handler,
    52  	externalRouter *mux.Router,
    53  	externalHandler http.Handler,
    54  	authMiddleware middleware.Interface,
    55  ) (serve services.Service, err error) {
    56  
    57  	// Create a couple Middlewares used to handle panics, perform auth, parse forms in http request, and set content type in response
    58  	handlerMiddleware := middleware.Merge(
    59  		httpreq.ExtractQueryTagsMiddleware(),
    60  		serverutil.RecoveryHTTPMiddleware,
    61  		authMiddleware,
    62  		serverutil.NewPrepopulateMiddleware(),
    63  		serverutil.ResponseJSONMiddleware(),
    64  	)
    65  
    66  	internalRouter := mux.NewRouter()
    67  	for route, handler := range queryRoutesToHandlers {
    68  		internalRouter.Path(route).Methods("GET", "POST").Handler(handler)
    69  	}
    70  
    71  	// There are some routes which are always registered on the external router, add them now and
    72  	// wrap them with the externalMiddleware
    73  	for route, handler := range alwaysExternalRoutesToHandlers {
    74  		externalRouter.Path(route).Methods("GET", "POST").Handler(handlerMiddleware.Wrap(handler))
    75  	}
    76  
    77  	// If the querier is running standalone without the query-frontend or query-scheduler, we must register the internal
    78  	// HTTP handler externally (as it's the only handler that needs to register on querier routes) and provide the
    79  	// external Loki Server HTTP handler to the frontend worker to ensure requests it processes use the default
    80  	// middleware instrumentation.
    81  	if querierRunningStandalone(cfg) {
    82  
    83  		// First, register the internal querier handler with the external HTTP server
    84  		routes := make([]string, len(queryRoutesToHandlers))
    85  		var idx = 0
    86  		for route := range queryRoutesToHandlers {
    87  			routes[idx] = route
    88  			idx++
    89  		}
    90  
    91  		// Register routes externally
    92  		for _, route := range routes {
    93  			externalRouter.Path(route).Methods("GET", "POST").Handler(handlerMiddleware.Wrap(internalRouter))
    94  		}
    95  
    96  		//If no frontend or scheduler address has been configured, then there is no place for the
    97  		//querier worker to request work from, so no need to start a worker service
    98  		if (*cfg.QuerierWorkerConfig).FrontendAddress == "" && (*cfg.QuerierWorkerConfig).SchedulerAddress == "" {
    99  			return nil, nil
   100  		}
   101  
   102  		// If a frontend or scheduler address has been configured, return a querier worker service that uses
   103  		// the external Loki Server HTTP server, which has now has the internal handler's routes registered with it
   104  		return querier_worker.NewQuerierWorker(
   105  			*(cfg.QuerierWorkerConfig),
   106  			cfg.SchedulerRing,
   107  			httpgrpc_server.NewServer(externalHandler),
   108  			util_log.Logger,
   109  			reg,
   110  		)
   111  	}
   112  
   113  	// Since we must be running a querier with either a frontend and/or scheduler at this point, if no scheduler ring, frontend, or scheduler address
   114  	// is configured, Loki will default to using the frontend on localhost on it's own GRPC listening port.
   115  	if cfg.SchedulerRing == nil && (*cfg.QuerierWorkerConfig).FrontendAddress == "" && (*cfg.QuerierWorkerConfig).SchedulerAddress == "" {
   116  		address := fmt.Sprintf("127.0.0.1:%d", cfg.GrpcListenPort)
   117  		level.Warn(util_log.Logger).Log(
   118  			"msg", "Worker address is empty, attempting automatic worker configuration. If queries are unresponsive consider configuring the worker explicitly.",
   119  			"address", address)
   120  		cfg.QuerierWorkerConfig.FrontendAddress = address
   121  	}
   122  
   123  	// Add a middleware to extract the trace context and add a header.
   124  	var internalHandler http.Handler
   125  	internalHandler = nethttp.MiddlewareFunc(
   126  		opentracing.GlobalTracer(),
   127  		internalRouter.ServeHTTP,
   128  		nethttp.OperationNameFunc(func(r *http.Request) string {
   129  			return "internalQuerier"
   130  		}))
   131  
   132  	internalHandler = handlerMiddleware.Wrap(internalHandler)
   133  
   134  	//Return a querier worker pointed to the internal querier HTTP handler so there is not a conflict in routes between the querier
   135  	//and the query frontend
   136  	return querier_worker.NewQuerierWorker(
   137  		*(cfg.QuerierWorkerConfig),
   138  		cfg.SchedulerRing,
   139  		httpgrpc_server.NewServer(internalHandler),
   140  		util_log.Logger,
   141  		reg,
   142  	)
   143  }
   144  
   145  func querierRunningStandalone(cfg WorkerServiceConfig) bool {
   146  	runningStandalone := !cfg.QueryFrontendEnabled && !cfg.QuerySchedulerEnabled && !cfg.ReadEnabled && !cfg.AllEnabled
   147  	level.Debug(util_log.Logger).Log(
   148  		"msg", "determining if querier is running as standalone target",
   149  		"runningStandalone", runningStandalone,
   150  		"queryFrontendEnabled", cfg.QueryFrontendEnabled,
   151  		"queryScheduleEnabled", cfg.QuerySchedulerEnabled,
   152  		"readEnabled", cfg.ReadEnabled,
   153  		"allEnabled", cfg.AllEnabled,
   154  	)
   155  
   156  	return runningStandalone
   157  }