func subscribeWebsocket(resp http.ResponseWriter, req *http.Request, topic string, useNewPublisher func(pubsub.Publisher) error) { updates, err := pubsub.Subscribe(topic, useNewPublisher) if err != nil { log.Println("subscribing for updates on", topic, "failed:", err) resp.WriteHeader(http.StatusInternalServerError) return } conn, err := upgrader.Upgrade(resp, req, nil) if err != nil { log.Println(err) return } viewer := &Viewer{ Conn: conn, ServerAddress: topic, Updates: updates, } viewer.writeUpdatesUntilClose() pubsub.Unsubscribe(updates, topic) _ = viewer.Close() }
// refresh list and update clients, then do both periodically forever func (ms *MasterServerPoller) loop() { defer ms.Close() defer func() { for addr, updates := range ms.Subscriptions { pubsub.Unsubscribe(updates, addr) } }() err := ms.refreshServers() if err != nil { log.Println("error getting initial master server list:", err) return } err = ms.update() if err != nil { log.Println("error publishing first update:", err) return } masterErrorCount := 0 errorCount := 0 updateTicker := time.NewTicker(5 * time.Second) defer updateTicker.Stop() refreshTicker := time.NewTicker(30 * time.Second) defer refreshTicker.Stop() for { select { case <-refreshTicker.C: err := ms.refreshServers() if err != nil { log.Println(err) masterErrorCount++ if masterErrorCount > 10 { log.Println("problem with master server, exiting master server loop") return } } else { masterErrorCount = 0 } case <-updateTicker.C: err := ms.update() if err != nil { log.Println(err) errorCount++ if errorCount > 10 { log.Println("problem with updates, exiting master server loop") return } } else { errorCount = 0 } case upd := <-ms.ServerUpdates: ms.storeServerUpdate(upd) case <-ms.Stop: return } } }
func (ms *MasterServerPoller) refreshServers() error { // open connection conn, err := net.DialTimeout("tcp", ms.Address, 15*time.Second) if err != nil { return err } defer conn.Close() in := bufio.NewScanner(conn) out := bufio.NewWriter(conn) // request new list _, err = out.WriteString("list\n") if err != nil { return err } err = out.Flush() if err != nil { return err } updatedList := map[string]extinfo.BasicInfo{} // process response for in.Scan() { msg := in.Text() if msg == "\x00" { // end of list continue } msg = strings.TrimPrefix(msg, "addserver ") msg = strings.TrimSpace(msg) addr := strings.Replace(msg, " ", ":", -1) // keep old state if possible oldState, known := ms.ServerStates[addr] updatedList[addr] = oldState if !known { // subscribe to updates if the server is new updates, err := pubsub.Subscribe(addr, func(publisher pubsub.Publisher) error { return NewServerPoller( publisher, func(sp *ServerPoller) { sp.Address = addr }, ) }) if err != nil { log.Println("error subscribing to updates on "+addr+":", err) } ms.Subscriptions[addr] = updates go func(updates <-chan pubsub.Update) { for upd := range updates { ms.ServerUpdates <- upd } }(updates) } } // unsubscribe from updates about servers not on the list anymore for addr := range ms.ServerStates { if _, ok := updatedList[addr]; !ok { pubsub.Unsubscribe(ms.ServerUpdates, addr) delete(ms.Subscriptions, addr) } } ms.ServerStates = updatedList return in.Err() }