mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-06 22:40:31 -05:00
Add common method for checking host availability
This commit is contained in:
parent
ce8ee831b3
commit
a7766c9033
5 changed files with 50 additions and 18 deletions
|
@ -25,11 +25,11 @@ type Random struct{}
|
|||
// Select selects an up host at random from the specified pool.
|
||||
func (r *Random) Select(pool HostPool) *UpstreamHost {
|
||||
// instead of just generating a random index
|
||||
// this is done to prevent selecting a down host
|
||||
// this is done to prevent selecting a unavailable host
|
||||
var randHost *UpstreamHost
|
||||
count := 0
|
||||
for _, host := range pool {
|
||||
if host.Down() {
|
||||
if !host.Available() {
|
||||
continue
|
||||
}
|
||||
count++
|
||||
|
@ -56,7 +56,7 @@ func (r *LeastConn) Select(pool HostPool) *UpstreamHost {
|
|||
count := 0
|
||||
leastConn := int64(1<<63 - 1)
|
||||
for _, host := range pool {
|
||||
if host.Down() {
|
||||
if !host.Available() {
|
||||
continue
|
||||
}
|
||||
hostConns := host.Conns
|
||||
|
@ -90,11 +90,11 @@ func (r *RoundRobin) Select(pool HostPool) *UpstreamHost {
|
|||
poolLen := uint32(len(pool))
|
||||
selection := atomic.AddUint32(&r.Robin, 1) % poolLen
|
||||
host := pool[selection]
|
||||
// if the currently selected host is down, just ffwd to up host
|
||||
for i := uint32(1); host.Down() && i < poolLen; i++ {
|
||||
// if the currently selected host is not available, just ffwd to up host
|
||||
for i := uint32(1); !host.Available() && i < poolLen; i++ {
|
||||
host = pool[(selection+i)%poolLen]
|
||||
}
|
||||
if host.Down() {
|
||||
if !host.Available() {
|
||||
return nil
|
||||
}
|
||||
return host
|
||||
|
|
|
@ -53,12 +53,23 @@ func TestRoundRobinPolicy(t *testing.T) {
|
|||
if h != pool[2] {
|
||||
t.Error("Expected second round robin host to be third host in the pool.")
|
||||
}
|
||||
// mark host as down
|
||||
pool[0].Unhealthy = true
|
||||
h = rrPolicy.Select(pool)
|
||||
if h != pool[1] {
|
||||
if h != pool[0] {
|
||||
t.Error("Expected third round robin host to be first host in the pool.")
|
||||
}
|
||||
// mark host as down
|
||||
pool[1].Unhealthy = true
|
||||
h = rrPolicy.Select(pool)
|
||||
if h != pool[2] {
|
||||
t.Error("Expected to skip down host.")
|
||||
}
|
||||
// mark host as full
|
||||
pool[2].Conns = 1
|
||||
pool[2].MaxConns = 1
|
||||
h = rrPolicy.Select(pool)
|
||||
if h != pool[0] {
|
||||
t.Error("Expected to skip full host.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLeastConnPolicy(t *testing.T) {
|
||||
|
|
|
@ -44,6 +44,7 @@ type UpstreamHost struct {
|
|||
ExtraHeaders http.Header
|
||||
CheckDown UpstreamHostDownFunc
|
||||
WithoutPathPrefix string
|
||||
MaxConns int64
|
||||
}
|
||||
|
||||
// Down checks whether the upstream host is down or not.
|
||||
|
@ -57,6 +58,16 @@ func (uh *UpstreamHost) Down() bool {
|
|||
return uh.CheckDown(uh)
|
||||
}
|
||||
|
||||
// Full checks whether the upstream host has reached its maximum connections
|
||||
func (uh *UpstreamHost) Full() bool {
|
||||
return uh.MaxConns > 0 && uh.Conns >= uh.MaxConns
|
||||
}
|
||||
|
||||
// Available checks whether the upstream host is available for proxying to
|
||||
func (uh *UpstreamHost) Available() bool {
|
||||
return !uh.Down() && !uh.Full()
|
||||
}
|
||||
|
||||
// tryDuration is how long to try upstream hosts; failures result in
|
||||
// immediate retries until this duration ends or we get a nil host.
|
||||
var tryDuration = 60 * time.Second
|
||||
|
|
|
@ -80,10 +80,6 @@ func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) {
|
|||
ExtraHeaders: upstream.proxyHeaders,
|
||||
CheckDown: func(upstream *staticUpstream) UpstreamHostDownFunc {
|
||||
return func(uh *UpstreamHost) bool {
|
||||
if upstream.MaxConns != 0 &&
|
||||
uh.Conns >= upstream.MaxConns {
|
||||
return true
|
||||
}
|
||||
if uh.Unhealthy {
|
||||
return true
|
||||
}
|
||||
|
@ -95,6 +91,7 @@ func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) {
|
|||
}
|
||||
}(upstream),
|
||||
WithoutPathPrefix: upstream.WithoutPathPrefix,
|
||||
MaxConns: upstream.MaxConns,
|
||||
}
|
||||
if baseURL, err := url.Parse(uh.Name); err == nil {
|
||||
uh.ReverseProxy = NewSingleHostReverseProxy(baseURL, uh.WithoutPathPrefix)
|
||||
|
@ -234,19 +231,19 @@ func (u *staticUpstream) HealthCheckWorker(stop chan struct{}) {
|
|||
func (u *staticUpstream) Select() *UpstreamHost {
|
||||
pool := u.Hosts
|
||||
if len(pool) == 1 {
|
||||
if pool[0].Down() {
|
||||
if !pool[0].Available() {
|
||||
return nil
|
||||
}
|
||||
return pool[0]
|
||||
}
|
||||
allDown := true
|
||||
allUnavailable := true
|
||||
for _, host := range pool {
|
||||
if !host.Down() {
|
||||
allDown = false
|
||||
if host.Available() {
|
||||
allUnavailable = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allDown {
|
||||
if allUnavailable {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,19 @@ func TestSelect(t *testing.T) {
|
|||
if h := upstream.Select(); h == nil {
|
||||
t.Error("Expected select to not return nil")
|
||||
}
|
||||
upstream.Hosts[0].Conns = 1
|
||||
upstream.Hosts[0].MaxConns = 1
|
||||
upstream.Hosts[1].Conns = 1
|
||||
upstream.Hosts[1].MaxConns = 1
|
||||
upstream.Hosts[2].Conns = 1
|
||||
upstream.Hosts[2].MaxConns = 1
|
||||
if h := upstream.Select(); h != nil {
|
||||
t.Error("Expected select to return nil as all hosts are full")
|
||||
}
|
||||
upstream.Hosts[2].Conns = 0
|
||||
if h := upstream.Select(); h == nil {
|
||||
t.Error("Expected select to not return nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterPolicy(t *testing.T) {
|
||||
|
|
Loading…
Reference in a new issue