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 }