Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 05:15 UTC
reverseproxy: Minor fixes and cleanup
Now use context cancellation to stop active health checker, which is simpler than and just as effective as using a separate stop channel.
Matthew Holt committed
Aug 7, 2020 at 18:02 UTC
e2f913bb7f813ac2f79cd3644066eff3815accd4
3 files changed
+47
-45
modules/caddyhttp/reverseproxy/healthchecks.go
+4
-7
@@ -78,7 +78,6 @@ type ActiveHealthChecks struct {
78
// body of a healthy backend.
79
ExpectBody string `json:"expect_body,omitempty"`
80
81
- stopChan chan struct{}
81
httpClient *http.Client
82
bodyRegexp *regexp.Regexp
83
logger *zap.Logger
@@ -137,8 +136,7 @@ func (h *Handler) activeHealthChecker() {
136
select {
137
case <-ticker.C:
138
h.doActiveHealthCheckForAllHosts()
140
- case <-h.HealthChecks.Active.stopChan:
141
- // TODO: consider using a Context for cancellation instead
139
+ case <-h.ctx.Done():
140
ticker.Stop()
141
return
142
}
@@ -341,8 +339,8 @@ func (h *Handler) countFailure(upstream *Upstream) {
339
if err != nil {
340
h.HealthChecks.Passive.logger.Error("could not count failure",
341
zap.String("host", upstream.Dial),
344
- zap.Error(err),
345
- )
342
+ zap.Error(err))
343
+ return
344
}
345
346
// forget it later
@@ -357,8 +355,7 @@ func (h *Handler) countFailure(upstream *Upstream) {
355
if err != nil {
356
h.HealthChecks.Passive.logger.Error("could not forget failure",
357
zap.String("host", upstream.Dial),
360
- zap.Error(err),
361
- )
358
+ zap.Error(err))
359
}
360
}(upstream.Host, failDuration)
361
}
modules/caddyhttp/reverseproxy/hosts.go
+1
-1
@@ -177,7 +177,7 @@ func (u *Upstream) fillDialInfo(r *http.Request) (DialInfo, error) {
177
// of the state of a remote host. It implements the
178
// Host interface.
179
type upstreamHost struct {
180
- numRequests int64 // must be first field to be 64-bit aligned on 32-bit systems (see https://golang.org/pkg/sync/atomic/#pkg-note-BUG)
180
+ numRequests int64 // must be 64-bit aligned on 32-bit systems (see https://golang.org/pkg/sync/atomic/#pkg-note-BUG)
181
fails int64
182
unhealthy int32
183
}
modules/caddyhttp/reverseproxy/reverseproxy.go
+42
-37
@@ -112,6 +112,7 @@ type Handler struct {
112
Transport http.RoundTripper `json:"-"`
113
CB CircuitBreaker `json:"-"`
114
115
+ ctx caddy.Context
116
logger *zap.Logger
117
}
118
@@ -125,6 +126,7 @@ func (Handler) CaddyModule() caddy.ModuleInfo {
126
127
// Provision ensures that h is set up properly before use.
128
func (h *Handler) Provision(ctx caddy.Context) error {
129
+ h.ctx = ctx
130
h.logger = ctx.Logger(h)
131
132
// start by loading modules
@@ -235,36 +237,43 @@ func (h *Handler) Provision(ctx caddy.Context) error {
237
}
238
}
239
238
- // if active health checks are enabled, configure them and start a worker
239
- if h.HealthChecks != nil &&
240
- h.HealthChecks.Active != nil &&
241
- (h.HealthChecks.Active.Path != "" || h.HealthChecks.Active.Port != 0) {
242
- h.HealthChecks.Active.logger = h.logger.Named("health_checker.active")
243
-
244
- timeout := time.Duration(h.HealthChecks.Active.Timeout)
245
- if timeout == 0 {
246
- timeout = 5 * time.Second
240
+ if h.HealthChecks != nil {
241
+ // set defaults on passive health checks, if necessary
242
+ if h.HealthChecks.Passive != nil {
243
+ if h.HealthChecks.Passive.FailDuration > 0 && h.HealthChecks.Passive.MaxFails == 0 {
244
+ h.HealthChecks.Passive.MaxFails = 1
245
+ }
246
}
247
249
- h.HealthChecks.Active.stopChan = make(chan struct{})
250
- h.HealthChecks.Active.httpClient = &http.Client{
251
- Timeout: timeout,
252
- Transport: h.Transport,
253
- }
248
+ // if active health checks are enabled, configure them and start a worker
249
+ if h.HealthChecks.Active != nil &&
250
+ (h.HealthChecks.Active.Path != "" || h.HealthChecks.Active.Port != 0) {
251
+ h.HealthChecks.Active.logger = h.logger.Named("health_checker.active")
252
255
- if h.HealthChecks.Active.Interval == 0 {
256
- h.HealthChecks.Active.Interval = caddy.Duration(30 * time.Second)
257
- }
253
+ timeout := time.Duration(h.HealthChecks.Active.Timeout)
254
+ if timeout == 0 {
255
+ timeout = 5 * time.Second
256
+ }
257
259
- if h.HealthChecks.Active.ExpectBody != "" {
260
- var err error
261
- h.HealthChecks.Active.bodyRegexp, err = regexp.Compile(h.HealthChecks.Active.ExpectBody)
262
- if err != nil {
263
- return fmt.Errorf("expect_body: compiling regular expression: %v", err)
258
+ h.HealthChecks.Active.httpClient = &http.Client{
259
+ Timeout: timeout,
260
+ Transport: h.Transport,
261
+ }
262
+
263
+ if h.HealthChecks.Active.Interval == 0 {
264
+ h.HealthChecks.Active.Interval = caddy.Duration(30 * time.Second)
265
+ }
266
+
267
+ if h.HealthChecks.Active.ExpectBody != "" {
268
+ var err error
269
+ h.HealthChecks.Active.bodyRegexp, err = regexp.Compile(h.HealthChecks.Active.ExpectBody)
270
+ if err != nil {
271
+ return fmt.Errorf("expect_body: compiling regular expression: %v", err)
272
+ }
273
}
265
- }
274
267
- go h.activeHealthChecker()
275
+ go h.activeHealthChecker()
276
+ }
277
}
278
279
// set up any response routes
@@ -280,14 +289,6 @@ func (h *Handler) Provision(ctx caddy.Context) error {
289
290
// Cleanup cleans up the resources made by h during provisioning.
291
func (h *Handler) Cleanup() error {
283
- // stop the active health checker
284
- if h.HealthChecks != nil &&
285
- h.HealthChecks.Active != nil &&
286
- h.HealthChecks.Active.stopChan != nil {
287
- // TODO: consider using context cancellation, could be much simpler
288
- close(h.HealthChecks.Active.stopChan)
289
- }
290
-
292
// TODO: Close keepalive connections on reload? https://github.com/caddyserver/caddy/pull/2507/files#diff-70219fd88fe3f36834f474ce6537ed26R762
293
294
// remove hosts from our config from the pool
@@ -351,7 +352,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
352
if proxyErr == nil {
353
proxyErr = fmt.Errorf("no upstreams available")
354
}
354
- if !h.LoadBalancing.tryAgain(start, proxyErr, r) {
355
+ if !h.LoadBalancing.tryAgain(h.ctx, start, proxyErr, r) {
356
break
357
}
358
continue
@@ -410,7 +411,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
411
h.countFailure(upstream)
412
413
// if we've tried long enough, break
413
- if !h.LoadBalancing.tryAgain(start, proxyErr, r) {
414
+ if !h.LoadBalancing.tryAgain(h.ctx, start, proxyErr, r) {
415
break
416
}
417
}
@@ -661,7 +662,7 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, di Dia
662
// long enough before the next retry (i.e. no more sleeping is
663
// needed). If false is returned, the handler should stop trying to
664
// proxy the request.
664
-func (lb LoadBalancing) tryAgain(start time.Time, proxyErr error, req *http.Request) bool {
665
+func (lb LoadBalancing) tryAgain(ctx caddy.Context, start time.Time, proxyErr error, req *http.Request) bool {
666
// if we've tried long enough, break
667
if time.Since(start) >= time.Duration(lb.TryDuration) {
668
return false
@@ -687,8 +688,12 @@ func (lb LoadBalancing) tryAgain(start time.Time, proxyErr error, req *http.Requ
688
}
689
690
// otherwise, wait and try the next available host
690
- time.Sleep(time.Duration(lb.TryInterval))
691
- return true
691
+ select {
692
+ case <-time.After(time.Duration(lb.TryInterval)):
693
+ return true
694
+ case <-ctx.Done():
695
+ return false
696
+ }
697
}
698
699
// directRequest modifies only req.URL so that it points to the upstream