healhcheck: various cleanups (#1106)

* healhcheck: various cleanups

Network wasn't used. IgnorePaths wasn't used. Move checkdown function to
common function shared between proxy protocols. And some naming fixed.

Also reset the Fails on a succesful healthcheck back to 0.

remove newlines from log

* compile

* fix test
This commit is contained in:
Miek Gieben 2017-09-24 19:37:43 +01:00 committed by GitHub
parent 102cfbd7fe
commit 148a99442d
8 changed files with 65 additions and 118 deletions

View file

@ -23,10 +23,8 @@ type apiProxy struct {
func (p *proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (p *proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
upstream := p.Select() upstream := p.Select()
network := "tcp" network := "tcp"
if upstream.Network != "" {
network = upstream.Network
}
address := upstream.Name address := upstream.Name
d, err := net.Dial(network, address) d, err := net.Dial(network, address)
if err != nil { if err != nil {
log.Printf("[ERROR] Unable to establish connection to upstream %s://%s: %s", network, address, err) log.Printf("[ERROR] Unable to establish connection to upstream %s://%s: %s", network, address, err)

View file

@ -190,9 +190,9 @@ func (k *Kubernetes) getClientConfig() (*rest.Config, error) {
down := false down := false
uh.CheckMu.Lock() uh.Lock()
until := uh.OkUntil until := uh.OkUntil
uh.CheckMu.Unlock() uh.Unlock()
if !until.IsZero() && time.Now().After(until) { if !until.IsZero() && time.Now().After(until) {
down = true down = true

View file

@ -17,17 +17,15 @@ type UpstreamHostDownFunc func(*UpstreamHost) bool
// UpstreamHost represents a single proxy upstream // UpstreamHost represents a single proxy upstream
type UpstreamHost struct { type UpstreamHost struct {
Conns int64 // must be first field to be 64-bit aligned on 32-bit systems Conns int64 // must be first field to be 64-bit aligned on 32-bit systems
Name string // IP address (and port) of this upstream host Name string // IP address (and port) of this upstream host
Network string // Network (tcp, unix, etc) of the host, default "" is "tcp" Fails int32
Fails int32 FailTimeout time.Duration
FailTimeout time.Duration OkUntil time.Time
OkUntil time.Time CheckDown UpstreamHostDownFunc
CheckDown UpstreamHostDownFunc CheckURL string
CheckURL string Checking bool
WithoutPathPrefix string sync.Mutex
Checking bool
CheckMu sync.Mutex
} }
// Down checks whether the upstream host is down or not. // Down checks whether the upstream host is down or not.
@ -39,9 +37,9 @@ func (uh *UpstreamHost) Down() bool {
fails := atomic.LoadInt32(&uh.Fails) fails := atomic.LoadInt32(&uh.Fails)
after := false after := false
uh.CheckMu.Lock() uh.Lock()
until := uh.OkUntil until := uh.OkUntil
uh.CheckMu.Unlock() uh.Unlock()
if !until.IsZero() && time.Now().After(until) { if !until.IsZero() && time.Now().After(until) {
after = true after = true
@ -106,45 +104,45 @@ func (u *HealthCheck) Stop() error {
// otherwise checks will back up, potentially a lot of them if a host is // otherwise checks will back up, potentially a lot of them if a host is
// absent for a long time. This arrangement makes checks quickly see if // absent for a long time. This arrangement makes checks quickly see if
// they are the only one running and abort otherwise. // they are the only one running and abort otherwise.
func healthCheckURL(nextTs time.Time, host *UpstreamHost) { func (uh *UpstreamHost) healthCheckURL(nextTs time.Time) {
// lock for our bool check. We don't just defer the unlock because // lock for our bool check. We don't just defer the unlock because
// we don't want the lock held while http.Get runs // we don't want the lock held while http.Get runs
host.CheckMu.Lock() uh.Lock()
// are we mid check? Don't run another one // are we mid check? Don't run another one
if host.Checking { if uh.Checking {
host.CheckMu.Unlock() uh.Unlock()
return return
} }
host.Checking = true uh.Checking = true
host.CheckMu.Unlock() uh.Unlock()
//log.Printf("[DEBUG] Healthchecking %s, nextTs is %s\n", url, nextTs.Local())
// fetch that url. This has been moved into a go func because // fetch that url. This has been moved into a go func because
// when the remote host is not merely not serving, but actually // when the remote host is not merely not serving, but actually
// absent, then tcp syn timeouts can be very long, and so one // absent, then tcp syn timeouts can be very long, and so one
// fetch could last several check intervals // fetch could last several check intervals
if r, err := http.Get(host.CheckURL); err == nil { if r, err := http.Get(uh.CheckURL); err == nil {
io.Copy(ioutil.Discard, r.Body) io.Copy(ioutil.Discard, r.Body)
r.Body.Close() r.Body.Close()
if r.StatusCode < 200 || r.StatusCode >= 400 { if r.StatusCode < 200 || r.StatusCode >= 400 {
log.Printf("[WARNING] Host %s health check returned HTTP code %d\n", log.Printf("[WARNING] Host %s health check returned HTTP code %d", uh.Name, r.StatusCode)
host.Name, r.StatusCode)
nextTs = time.Unix(0, 0) nextTs = time.Unix(0, 0)
} else {
// We are healthy again, reset fails
atomic.StoreInt32(&uh.Fails, 0)
} }
} else { } else {
log.Printf("[WARNING] Host %s health check probe failed: %v\n", host.Name, err) log.Printf("[WARNING] Host %s health check probe failed: %v", uh.Name, err)
nextTs = time.Unix(0, 0) nextTs = time.Unix(0, 0)
} }
host.CheckMu.Lock() uh.Lock()
host.Checking = false uh.Checking = false
host.OkUntil = nextTs uh.OkUntil = nextTs
host.CheckMu.Unlock() uh.Unlock()
} }
func (u *HealthCheck) healthCheck() { func (u *HealthCheck) healthCheck() {
@ -174,11 +172,11 @@ func (u *HealthCheck) healthCheck() {
host.CheckURL = "http://" + net.JoinHostPort(checkHostName, checkPort) + u.Path host.CheckURL = "http://" + net.JoinHostPort(checkHostName, checkPort) + u.Path
} }
// calculate this before the get // calculate next timestamp before the get
nextTs := time.Now().Add(u.Future) nextTs := time.Now().Add(u.Future)
// locks/bools should prevent requests backing up // locks/bools should prevent requests backing up
go healthCheckURL(nextTs, host) go host.healthCheckURL(nextTs)
} }
} }

30
plugin/proxy/down.go Normal file
View file

@ -0,0 +1,30 @@
package proxy
import (
"sync/atomic"
"time"
"github.com/coredns/coredns/plugin/pkg/healthcheck"
)
// Default CheckDown functions for use in the proxy plugin.
var checkDownFunc = func(upstream *staticUpstream) healthcheck.UpstreamHostDownFunc {
return func(uh *healthcheck.UpstreamHost) bool {
down := false
uh.Lock()
until := uh.OkUntil
uh.Unlock()
if !until.IsZero() && time.Now().After(until) {
down = true
}
fails := atomic.LoadInt32(&uh.Fails)
if fails >= upstream.MaxFails && upstream.MaxFails != 0 {
down = true
}
return down
}
}

View file

@ -10,7 +10,6 @@ import (
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"sync/atomic"
"time" "time"
"github.com/coredns/coredns/plugin/pkg/healthcheck" "github.com/coredns/coredns/plugin/pkg/healthcheck"
@ -198,7 +197,6 @@ func newUpstream(hosts []string, old *staticUpstream) Upstream {
Future: 60 * time.Second, Future: 60 * time.Second,
}, },
ex: old.ex, ex: old.ex,
WithoutPathPrefix: old.WithoutPathPrefix,
IgnoredSubDomains: old.IgnoredSubDomains, IgnoredSubDomains: old.IgnoredSubDomains,
} }
@ -209,28 +207,7 @@ func newUpstream(hosts []string, old *staticUpstream) Upstream {
Conns: 0, Conns: 0,
Fails: 0, Fails: 0,
FailTimeout: upstream.FailTimeout, FailTimeout: upstream.FailTimeout,
CheckDown: checkDownFunc(upstream),
CheckDown: func(upstream *staticUpstream) healthcheck.UpstreamHostDownFunc {
return func(uh *healthcheck.UpstreamHost) bool {
down := false
uh.CheckMu.Lock()
until := uh.OkUntil
uh.CheckMu.Unlock()
if !until.IsZero() && time.Now().After(until) {
down = true
}
fails := atomic.LoadInt32(&uh.Fails)
if fails >= upstream.MaxFails && upstream.MaxFails != 0 {
down = true
}
return down
}
}(upstream),
WithoutPathPrefix: upstream.WithoutPathPrefix,
} }
upstream.Hosts[i] = uh upstream.Hosts[i] = uh

View file

@ -40,28 +40,7 @@ func NewLookupWithOption(hosts []string, opts Options) Proxy {
Conns: 0, Conns: 0,
Fails: 0, Fails: 0,
FailTimeout: upstream.FailTimeout, FailTimeout: upstream.FailTimeout,
CheckDown: checkDownFunc(upstream),
CheckDown: func(upstream *staticUpstream) healthcheck.UpstreamHostDownFunc {
return func(uh *healthcheck.UpstreamHost) bool {
down := false
uh.CheckMu.Lock()
until := uh.OkUntil
uh.CheckMu.Unlock()
if !until.IsZero() && time.Now().After(until) {
down = true
}
fails := atomic.LoadInt32(&uh.Fails)
if fails >= upstream.MaxFails && upstream.MaxFails != 0 {
down = true
}
return down
}
}(upstream),
WithoutPathPrefix: upstream.WithoutPathPrefix,
} }
upstream.Hosts[i] = uh upstream.Hosts[i] = uh

View file

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"net" "net"
"strconv" "strconv"
"sync/atomic"
"time" "time"
"github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin"
@ -20,7 +19,6 @@ type staticUpstream struct {
healthcheck.HealthCheck healthcheck.HealthCheck
WithoutPathPrefix string
IgnoredSubDomains []string IgnoredSubDomains []string
ex Exchanger ex Exchanger
} }
@ -69,28 +67,7 @@ func NewStaticUpstreams(c *caddyfile.Dispenser) ([]Upstream, error) {
Conns: 0, Conns: 0,
Fails: 0, Fails: 0,
FailTimeout: upstream.FailTimeout, FailTimeout: upstream.FailTimeout,
CheckDown: checkDownFunc(upstream),
CheckDown: func(upstream *staticUpstream) healthcheck.UpstreamHostDownFunc {
return func(uh *healthcheck.UpstreamHost) bool {
down := false
uh.CheckMu.Lock()
until := uh.OkUntil
uh.CheckMu.Unlock()
if !until.IsZero() && time.Now().After(until) {
down = true
}
fails := atomic.LoadInt32(&uh.Fails)
if fails >= upstream.MaxFails && upstream.MaxFails != 0 {
down = true
}
return down
}
}(upstream),
WithoutPathPrefix: upstream.WithoutPathPrefix,
} }
upstream.Hosts[i] = uh upstream.Hosts[i] = uh
@ -158,11 +135,6 @@ func parseBlock(c *caddyfile.Dispenser, u *staticUpstream) error {
u.Future = 3 * time.Second u.Future = 3 * time.Second
} }
} }
case "without":
if !c.NextArg() {
return c.ArgErr()
}
u.WithoutPathPrefix = c.Val()
case "except": case "except":
ignoredDomains := c.RemainingArgs() ignoredDomains := c.RemainingArgs()
if len(ignoredDomains) == 0 { if len(ignoredDomains) == 0 {

View file

@ -84,13 +84,6 @@ proxy . 8.8.8.8:53 {
}, },
{ {
` `
proxy . 8.8.8.8:53 {
without without
}`,
false,
},
{
`
proxy . 8.8.8.8:53 { proxy . 8.8.8.8:53 {
except miek.nl example.org 10.0.0.0/24 except miek.nl example.org 10.0.0.0/24
}`, }`,