package main import ( "fmt" "log" "net" "sync" "time" ) type DiscardedHost struct { Host string Tries int } type DiscardedHostHandler struct { mu sync.RWMutex port int hosts []*DiscardedHost onConnection *func(string) } func (d *DiscardedHostHandler) run() { for range time.Tick(time.Second) { d.mu.RLock() lst := make([]*DiscardedHost, 0, len(d.hosts)) for _, host := range d.hosts { if host.Tries >= 0 && host.Tries < 5 { go d.testConnection(host) lst = append(lst, host) } else { if host.Tries > 0 { log.Printf("Host %s discarded after %d tries", host.Host, host.Tries) } } } d.mu.RUnlock() d.mu.Lock() d.hosts = lst d.mu.Unlock() } } func (d *DiscardedHostHandler) testConnection(host *DiscardedHost) { addr := fmt.Sprintf("%s:%d", host.Host, d.port) conn, err := net.Dial("tcp", addr) if err != nil { host.Tries++ if host.Tries >= 5 { // Exceeded retry threshold; will be dropped by run loop. } } else { conn.Close() if d.onConnection != nil { fn := *d.onConnection fn(host.Host) } } } func NewDiscardedHostHandler(port int) *DiscardedHostHandler { ret := &DiscardedHostHandler{ hosts: make([]*DiscardedHost, 0), port: port, } go ret.run() return ret } func (d *DiscardedHostHandler) SetReconnectHandler(fn func(string)) { d.onConnection = &fn } func (d *DiscardedHostHandler) AppendHost(host string) { d.mu.Lock() defer d.mu.Unlock() log.Printf("Adding host %s to retry list", host) d.hosts = append(d.hosts, &DiscardedHost{ Host: host, Tries: 0, }) }