github.com/nsqio/nsq@v1.3.0/nsqd/lookup.go (about) 1 package nsqd 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "net" 7 "os" 8 "strconv" 9 "time" 10 11 "github.com/nsqio/go-nsq" 12 "github.com/nsqio/nsq/internal/version" 13 ) 14 15 func connectCallback(n *NSQD, hostname string) func(*lookupPeer) { 16 return func(lp *lookupPeer) { 17 ci := make(map[string]interface{}) 18 ci["version"] = version.Binary 19 ci["tcp_port"] = n.getOpts().BroadcastTCPPort 20 ci["http_port"] = n.getOpts().BroadcastHTTPPort 21 ci["hostname"] = hostname 22 ci["broadcast_address"] = n.getOpts().BroadcastAddress 23 24 cmd, err := nsq.Identify(ci) 25 if err != nil { 26 lp.Close() 27 return 28 } 29 30 resp, err := lp.Command(cmd) 31 if err != nil { 32 n.logf(LOG_ERROR, "LOOKUPD(%s): %s - %s", lp, cmd, err) 33 return 34 } else if bytes.Equal(resp, []byte("E_INVALID")) { 35 n.logf(LOG_INFO, "LOOKUPD(%s): lookupd returned %s", lp, resp) 36 lp.Close() 37 return 38 } 39 40 err = json.Unmarshal(resp, &lp.Info) 41 if err != nil { 42 n.logf(LOG_ERROR, "LOOKUPD(%s): parsing response - %s", lp, resp) 43 lp.Close() 44 return 45 } 46 n.logf(LOG_INFO, "LOOKUPD(%s): peer info %+v", lp, lp.Info) 47 if lp.Info.BroadcastAddress == "" { 48 n.logf(LOG_ERROR, "LOOKUPD(%s): no broadcast address", lp) 49 } 50 51 // build all the commands first so we exit the lock(s) as fast as possible 52 var commands []*nsq.Command 53 n.RLock() 54 for _, topic := range n.topicMap { 55 topic.RLock() 56 if len(topic.channelMap) == 0 { 57 commands = append(commands, nsq.Register(topic.name, "")) 58 } else { 59 for _, channel := range topic.channelMap { 60 commands = append(commands, nsq.Register(channel.topicName, channel.name)) 61 } 62 } 63 topic.RUnlock() 64 } 65 n.RUnlock() 66 67 for _, cmd := range commands { 68 n.logf(LOG_INFO, "LOOKUPD(%s): %s", lp, cmd) 69 _, err := lp.Command(cmd) 70 if err != nil { 71 n.logf(LOG_ERROR, "LOOKUPD(%s): %s - %s", lp, cmd, err) 72 return 73 } 74 } 75 } 76 } 77 78 func (n *NSQD) lookupLoop() { 79 var lookupPeers []*lookupPeer 80 var lookupAddrs []string 81 connect := true 82 83 hostname, err := os.Hostname() 84 if err != nil { 85 n.logf(LOG_FATAL, "failed to get hostname - %s", err) 86 os.Exit(1) 87 } 88 89 // for announcements, lookupd determines the host automatically 90 ticker := time.NewTicker(15 * time.Second) 91 defer ticker.Stop() 92 for { 93 if connect { 94 for _, host := range n.getOpts().NSQLookupdTCPAddresses { 95 if in(host, lookupAddrs) { 96 continue 97 } 98 n.logf(LOG_INFO, "LOOKUP(%s): adding peer", host) 99 lookupPeer := newLookupPeer(host, n.getOpts().MaxBodySize, n.logf, 100 connectCallback(n, hostname)) 101 lookupPeer.Command(nil) // start the connection 102 lookupPeers = append(lookupPeers, lookupPeer) 103 lookupAddrs = append(lookupAddrs, host) 104 } 105 n.lookupPeers.Store(lookupPeers) 106 connect = false 107 } 108 109 select { 110 case <-ticker.C: 111 // send a heartbeat and read a response (read detects closed conns) 112 for _, lookupPeer := range lookupPeers { 113 n.logf(LOG_DEBUG, "LOOKUPD(%s): sending heartbeat", lookupPeer) 114 cmd := nsq.Ping() 115 _, err := lookupPeer.Command(cmd) 116 if err != nil { 117 n.logf(LOG_ERROR, "LOOKUPD(%s): %s - %s", lookupPeer, cmd, err) 118 } 119 } 120 case val := <-n.notifyChan: 121 var cmd *nsq.Command 122 var branch string 123 124 switch val := val.(type) { 125 case *Channel: 126 // notify all nsqlookupds that a new channel exists, or that it's removed 127 branch = "channel" 128 channel := val 129 if channel.Exiting() { 130 cmd = nsq.UnRegister(channel.topicName, channel.name) 131 } else { 132 cmd = nsq.Register(channel.topicName, channel.name) 133 } 134 case *Topic: 135 // notify all nsqlookupds that a new topic exists, or that it's removed 136 branch = "topic" 137 topic := val 138 if topic.Exiting() { 139 cmd = nsq.UnRegister(topic.name, "") 140 } else { 141 cmd = nsq.Register(topic.name, "") 142 } 143 } 144 145 for _, lookupPeer := range lookupPeers { 146 n.logf(LOG_INFO, "LOOKUPD(%s): %s %s", lookupPeer, branch, cmd) 147 _, err := lookupPeer.Command(cmd) 148 if err != nil { 149 n.logf(LOG_ERROR, "LOOKUPD(%s): %s - %s", lookupPeer, cmd, err) 150 } 151 } 152 case <-n.optsNotificationChan: 153 var tmpPeers []*lookupPeer 154 var tmpAddrs []string 155 for _, lp := range lookupPeers { 156 if in(lp.addr, n.getOpts().NSQLookupdTCPAddresses) { 157 tmpPeers = append(tmpPeers, lp) 158 tmpAddrs = append(tmpAddrs, lp.addr) 159 continue 160 } 161 n.logf(LOG_INFO, "LOOKUP(%s): removing peer", lp) 162 lp.Close() 163 } 164 lookupPeers = tmpPeers 165 lookupAddrs = tmpAddrs 166 connect = true 167 case <-n.exitChan: 168 goto exit 169 } 170 } 171 172 exit: 173 n.logf(LOG_INFO, "LOOKUP: closing") 174 } 175 176 func in(s string, lst []string) bool { 177 for _, v := range lst { 178 if s == v { 179 return true 180 } 181 } 182 return false 183 } 184 185 func (n *NSQD) lookupdHTTPAddrs() []string { 186 var lookupHTTPAddrs []string 187 lookupPeers := n.lookupPeers.Load() 188 if lookupPeers == nil { 189 return nil 190 } 191 for _, lp := range lookupPeers.([]*lookupPeer) { 192 if len(lp.Info.BroadcastAddress) <= 0 { 193 continue 194 } 195 addr := net.JoinHostPort(lp.Info.BroadcastAddress, strconv.Itoa(lp.Info.HTTPPort)) 196 lookupHTTPAddrs = append(lookupHTTPAddrs, addr) 197 } 198 return lookupHTTPAddrs 199 }