diff options
Diffstat (limited to 'modules')
-rw-r--r-- | modules/caddyhttp/reverseproxy/circuitbreaker.go | 157 | ||||
-rw-r--r-- | modules/caddyhttp/reverseproxy/healthchecks.go | 3 |
2 files changed, 2 insertions, 158 deletions
diff --git a/modules/caddyhttp/reverseproxy/circuitbreaker.go b/modules/caddyhttp/reverseproxy/circuitbreaker.go deleted file mode 100644 index 830ab43..0000000 --- a/modules/caddyhttp/reverseproxy/circuitbreaker.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2015 Matthew Holt and The Caddy Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package reverseproxy - -import ( - "fmt" - "sync/atomic" - "time" - - "github.com/caddyserver/caddy/v2" - "github.com/vulcand/oxy/memmetrics" -) - -func init() { - caddy.RegisterModule(internalCircuitBreaker{}) -} - -// internalCircuitBreaker implements circuit breaking functionality -// for requests within this process over a sliding time window. -type internalCircuitBreaker struct { - tripped int32 - cbFactor int32 - threshold float64 - metrics *memmetrics.RTMetrics - tripTime time.Duration - Config -} - -// CaddyModule returns the Caddy module information. -func (internalCircuitBreaker) CaddyModule() caddy.ModuleInfo { - return caddy.ModuleInfo{ - ID: "http.reverse_proxy.circuit_breakers.internal", - New: func() caddy.Module { return new(internalCircuitBreaker) }, - } -} - -// Provision sets up a configured circuit breaker. -func (c *internalCircuitBreaker) Provision(ctx caddy.Context) error { - f, ok := typeCB[c.Factor] - if !ok { - return fmt.Errorf("type is not defined") - } - - if c.TripTime == "" { - c.TripTime = defaultTripTime - } - - tw, err := time.ParseDuration(c.TripTime) - if err != nil { - return fmt.Errorf("cannot parse trip_time duration, %v", err.Error()) - } - - mt, err := memmetrics.NewRTMetrics() - if err != nil { - return fmt.Errorf("cannot create new metrics: %v", err.Error()) - } - - c.cbFactor = f - c.tripTime = tw - c.threshold = c.Threshold - c.metrics = mt - c.tripped = 0 - - return nil -} - -// Ok returns whether the circuit breaker is tripped or not. -func (c *internalCircuitBreaker) Ok() bool { - tripped := atomic.LoadInt32(&c.tripped) - return tripped == 0 -} - -// RecordMetric records a response status code and execution time of a request. This function should be run in a separate goroutine. -func (c *internalCircuitBreaker) RecordMetric(statusCode int, latency time.Duration) { - c.metrics.Record(statusCode, latency) - c.checkAndSet() -} - -// Ok checks our metrics to see if we should trip our circuit breaker, or if the fallback duration has completed. -func (c *internalCircuitBreaker) checkAndSet() { - var isTripped bool - - switch c.cbFactor { - case factorErrorRatio: - // check if amount of network errors exceed threshold over sliding window, threshold for comparison should be < 1.0 i.e. .5 = 50th percentile - if c.metrics.NetworkErrorRatio() > c.threshold { - isTripped = true - } - case factorLatency: - // check if threshold in milliseconds is reached and trip - hist, err := c.metrics.LatencyHistogram() - if err != nil { - return - } - - l := hist.LatencyAtQuantile(c.threshold) - if l.Nanoseconds()/int64(time.Millisecond) > int64(c.threshold) { - isTripped = true - } - case factorStatusCodeRatio: - // check ratio of error status codes of sliding window, threshold for comparison should be < 1.0 i.e. .5 = 50th percentile - if c.metrics.ResponseCodeRatio(500, 600, 0, 600) > c.threshold { - isTripped = true - } - } - - if isTripped { - c.metrics.Reset() - atomic.AddInt32(&c.tripped, 1) - - // wait tripTime amount before allowing operations to resume. - t := time.NewTimer(c.tripTime) - <-t.C - - atomic.AddInt32(&c.tripped, -1) - } -} - -// Config represents the configuration of a circuit breaker. -type Config struct { - // The threshold over sliding window that would trip the circuit breaker - Threshold float64 `json:"threshold"` - // Possible values: latency, error_ratio, and status_ratio. It - // defaults to latency. - Factor string `json:"factor"` - // How long to wait after the circuit is tripped before allowing operations to resume. - // The default is 5s. - TripTime string `json:"trip_time"` -} - -const ( - factorLatency = iota + 1 - factorErrorRatio - factorStatusCodeRatio - defaultTripTime = "5s" -) - -var ( - // typeCB handles converting a Config Factor value to the internal circuit breaker types. - typeCB = map[string]int32{ - "latency": factorLatency, - "error_ratio": factorErrorRatio, - "status_ratio": factorStatusCodeRatio, - } -) diff --git a/modules/caddyhttp/reverseproxy/healthchecks.go b/modules/caddyhttp/reverseproxy/healthchecks.go index 85274c6..02d8438 100644 --- a/modules/caddyhttp/reverseproxy/healthchecks.go +++ b/modules/caddyhttp/reverseproxy/healthchecks.go @@ -113,7 +113,8 @@ type PassiveHealthChecks struct { // CircuitBreaker is a type that can act as an early-warning // system for the health checker when backends are getting -// overloaded. +// overloaded. This interface is still experimental and is +// subject to change. type CircuitBreaker interface { OK() bool RecordMetric(statusCode int, latency time.Duration) |