2ab5264595
rpcproxy is refactored into serverlist which prioritizes good servers over servers in a remote DC or who have had a failure. Registration, heartbeating, and alloc status updating will retry faster when new servers are discovered. Consul discovery will be retried more quickly when no servers are available (eg on startup or an outage).
112 lines
2.3 KiB
Go
112 lines
2.3 KiB
Go
package client
|
|
|
|
import (
|
|
"math/rand"
|
|
"net"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// serverlist is a prioritized randomized list of nomad servers. Users should
|
|
// call all() to retrieve the full list, followed by failed(e) on each endpoint
|
|
// that's failed and good(e) when a valid endpoint is found.
|
|
type serverlist struct {
|
|
e endpoints
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
func newServerList() *serverlist {
|
|
return &serverlist{}
|
|
}
|
|
|
|
// set the server list to a new list. The new list will be shuffled and sorted
|
|
// by priority.
|
|
func (s *serverlist) set(in endpoints) {
|
|
s.mu.Lock()
|
|
s.e = in
|
|
s.mu.Unlock()
|
|
}
|
|
|
|
// all returns a copy of the full server list, shuffled and then sorted by
|
|
// priority
|
|
func (s *serverlist) all() endpoints {
|
|
s.mu.RLock()
|
|
out := make(endpoints, len(s.e))
|
|
copy(out, s.e)
|
|
s.mu.RUnlock()
|
|
|
|
// Randomize the order
|
|
for i, j := range rand.Perm(len(out)) {
|
|
out[i], out[j] = out[j], out[i]
|
|
}
|
|
|
|
// Sort by priority
|
|
sort.Sort(out)
|
|
return out
|
|
}
|
|
|
|
// failed endpoint will be deprioritized if its still in the list.
|
|
func (s *serverlist) failed(e *endpoint) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
for _, cur := range s.e {
|
|
if cur.equal(e) {
|
|
cur.priority++
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// good endpoint will get promoted to the highest priority if it's still in the
|
|
// list.
|
|
func (s *serverlist) good(e *endpoint) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
for _, cur := range s.e {
|
|
if cur.equal(e) {
|
|
cur.priority = 0
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (e endpoints) Len() int {
|
|
return len(e)
|
|
}
|
|
|
|
func (e endpoints) Less(i int, j int) bool {
|
|
// Sort only by priority as endpoints should be shuffled and ordered
|
|
// only by priority
|
|
return e[i].priority < e[j].priority
|
|
}
|
|
|
|
func (e endpoints) Swap(i int, j int) {
|
|
e[i], e[j] = e[j], e[i]
|
|
}
|
|
|
|
type endpoints []*endpoint
|
|
|
|
func (e endpoints) String() string {
|
|
names := make([]string, 0, len(e))
|
|
for _, endpoint := range e {
|
|
names = append(names, endpoint.name)
|
|
}
|
|
return strings.Join(names, ",")
|
|
}
|
|
|
|
type endpoint struct {
|
|
name string
|
|
addr net.Addr
|
|
|
|
// 0 being the highest priority
|
|
priority int
|
|
}
|
|
|
|
// equal returns true if the name and addr match between two endpoints.
|
|
// Priority is ignored because the same endpoint may be added by discovery and
|
|
// heartbeating with different priorities.
|
|
func (e *endpoint) equal(o *endpoint) bool {
|
|
return e.name == o.name && e.addr == o.addr
|
|
}
|