@@ -22,6 +22,7 @@ import (
22
22
"github.com/prometheus/client_golang/prometheus"
23
23
"github.com/prometheus/client_golang/prometheus/promhttp"
24
24
"github.com/sirupsen/logrus"
25
+ "golang.org/x/sync/errgroup"
25
26
"google.golang.org/grpc"
26
27
"google.golang.org/grpc/credentials"
27
28
"google.golang.org/grpc/health/grpc_health_v1"
@@ -37,11 +38,7 @@ func New(name string, opts ...Option) (*Server, error) {
37
38
Name : name ,
38
39
options : options ,
39
40
}
40
-
41
- err = server .initializeDebug ()
42
- if err != nil {
43
- return nil , fmt .Errorf ("failed to initialize debug server: %w" , err )
44
- }
41
+ server .builtinServices = newBuiltinServices (server )
45
42
46
43
server .httpMux = http .NewServeMux ()
47
44
server .http = & http.Server {Handler : server .httpMux }
@@ -76,9 +73,7 @@ type Server struct {
76
73
77
74
options * options
78
75
79
- // debug is an HTTP server for debug endpoints - metrics, pprof, readiness & liveness.
80
- debug * http.Server
81
- debugListener net.Listener
76
+ builtinServices * builtinServices
82
77
83
78
// http is an http Server, only used when port is specified in cfg
84
79
http * http.Server
@@ -114,21 +109,13 @@ func (s *Server) ListenAndServe() error {
114
109
}
115
110
}()
116
111
117
- if srv := s . options . config . Services . Debug ; srv != nil {
118
- s . debugListener , err = net . Listen ( "tcp" , srv . Address )
112
+ go func () {
113
+ err := s . builtinServices . ListenAndServe ( )
119
114
if err != nil {
120
- return fmt .Errorf ("failed to start debug server: %w" , err )
115
+ s .Logger ().WithError (err ).Errorf ("builtin services encountered an error - closing remaining servers." )
116
+ s .Close ()
121
117
}
122
- s .debug .Addr = srv .Address
123
-
124
- go func () {
125
- err := serveHTTP (srv , s .debug , s .debugListener )
126
- if err != nil {
127
- s .Logger ().WithError (err ).Errorf ("debug server encountered an error - closing remaining servers." )
128
- s .Close ()
129
- }
130
- }()
131
- }
118
+ }()
132
119
133
120
if srv := s .options .config .Services .HTTP ; srv != nil {
134
121
s .httpListener , err = net .Listen ("tcp" , srv .Address )
@@ -229,16 +216,12 @@ func (s *Server) close(ctx context.Context) error {
229
216
s .Logger ().Info ("HTTP server terminated." )
230
217
}
231
218
232
- // Always terminate debug server last, we want to keep it running for as long as possible
233
- if s .debug != nil {
234
- err := s .debug .Shutdown (ctx )
235
- if err != nil {
236
- return fmt .Errorf ("failed to close debug server: %w" , err )
237
- }
238
- // s.http.Shutdown() also closes the underlying net.Listener, we just release the reference.
239
- s .debugListener = nil
240
- s .Logger ().Info ("Debug server terminated." )
219
+ // Always terminate builtin server last, we want to keep it running for as long as possible
220
+ err := s .builtinServices .Close ()
221
+ if err != nil {
222
+ return fmt .Errorf ("failed to close debug server: %w" , err )
241
223
}
224
+ s .Logger ().Info ("Debug server terminated." )
242
225
243
226
return nil
244
227
}
@@ -253,30 +236,19 @@ func (s *Server) isClosing() bool {
253
236
}
254
237
}
255
238
256
- func (s * Server ) initializeDebug () error {
257
- logger := s .Logger ().WithField ("protocol" , "debug" )
258
-
239
+ func (s * Server ) healthEndpoint () http.Handler {
259
240
mux := http .NewServeMux ()
260
-
261
241
mux .HandleFunc ("/ready" , s .options .healthHandler .ReadyEndpoint )
262
- logger .Debug ("Serving readiness handler on /ready" )
263
-
264
242
mux .HandleFunc ("/live" , s .options .healthHandler .LiveEndpoint )
265
- logger .Debug ("Serving liveliness handler on /live" )
243
+ return mux
244
+ }
266
245
246
+ func (s * Server ) metricsEndpoint () http.Handler {
247
+ mux := http .NewServeMux ()
267
248
mux .Handle ("/metrics" , promhttp .InstrumentMetricHandler (
268
249
s .options .metricsRegistry , promhttp .HandlerFor (s .options .metricsRegistry , promhttp.HandlerOpts {}),
269
250
))
270
- s .Logger ().WithField ("protocol" , "http" ).Debug ("Serving metrics on /metrics" )
271
-
272
- mux .Handle (pprof .Path , pprof .Handler ())
273
- logger .Debug ("Serving profiler on /debug/pprof" )
274
-
275
- s .debug = & http.Server {
276
- Handler : mux ,
277
- }
278
-
279
- return nil
251
+ return mux
280
252
}
281
253
282
254
func (s * Server ) initializeGRPC () error {
@@ -331,12 +303,87 @@ func httpAddress(cfg *ServerConfiguration, l net.Listener) string {
331
303
}
332
304
333
305
func (s * Server ) DebugAddress () string {
334
- return httpAddress (s .options .config .Services .Debug , s .debugListener )
306
+ if s .builtinServices == nil {
307
+ return ""
308
+ }
309
+ return "http://" + s .builtinServices .Debug .Addr
310
+ }
311
+ func (s * Server ) HealthAddr () string {
312
+ if s .builtinServices == nil {
313
+ return ""
314
+ }
315
+ return "http://" + s .builtinServices .Health .Addr
335
316
}
336
317
func (s * Server ) HTTPAddress () string {
337
318
return httpAddress (s .options .config .Services .HTTP , s .httpListener )
338
319
}
339
- func (s * Server ) ReadinessAddress () string {
340
- return s .DebugAddress ()
341
- }
342
320
func (s * Server ) GRPCAddress () string { return s .options .config .Services .GRPC .GetAddress () }
321
+
322
+ const (
323
+ BuiltinDebugPort = 6060
324
+ BuiltinMetricsPort = 9500
325
+ BuiltinHealthPort = 9501
326
+ )
327
+
328
+ type builtinServices struct {
329
+ underTest bool
330
+
331
+ Debug * http.Server
332
+ Health * http.Server
333
+ Metrics * http.Server
334
+ }
335
+
336
+ func newBuiltinServices (server * Server ) * builtinServices {
337
+ healthAddr := fmt .Sprintf (":%d" , BuiltinHealthPort )
338
+ if server .options .underTest {
339
+ healthAddr = "localhost:0"
340
+ }
341
+
342
+ return & builtinServices {
343
+ underTest : server .options .underTest ,
344
+ Debug : & http.Server {
345
+ Addr : fmt .Sprintf ("localhost:%d" , BuiltinDebugPort ),
346
+ Handler : pprof .Handler (),
347
+ },
348
+ Health : & http.Server {
349
+ Addr : healthAddr ,
350
+ Handler : server .healthEndpoint (),
351
+ },
352
+ Metrics : & http.Server {
353
+ Addr : fmt .Sprintf ("localhost:%d" , BuiltinMetricsPort ),
354
+ Handler : server .metricsEndpoint (),
355
+ },
356
+ }
357
+ }
358
+
359
+ func (s * builtinServices ) ListenAndServe () error {
360
+ if s == nil {
361
+ return nil
362
+ }
363
+
364
+ var eg errgroup.Group
365
+ if ! s .underTest {
366
+ eg .Go (func () error { return s .Debug .ListenAndServe () })
367
+ eg .Go (func () error { return s .Metrics .ListenAndServe () })
368
+ }
369
+ eg .Go (func () error {
370
+ // health is the only service which has a variable address,
371
+ // because we need the health service to figure out if the
372
+ // server started at all
373
+ l , err := net .Listen ("tcp" , s .Health .Addr )
374
+ if err != nil {
375
+ return err
376
+ }
377
+ s .Health .Addr = l .Addr ().String ()
378
+ return s .Health .Serve (l )
379
+ })
380
+ return eg .Wait ()
381
+ }
382
+
383
+ func (s * builtinServices ) Close () error {
384
+ var eg errgroup.Group
385
+ eg .Go (func () error { return s .Debug .Close () })
386
+ eg .Go (func () error { return s .Metrics .Close () })
387
+ eg .Go (func () error { return s .Health .Close () })
388
+ return eg .Wait ()
389
+ }
0 commit comments