// GetTopic performs a thread safe operation // to return a pointer to a Topic object (potentially new) func (n *NSQD) GetTopic(topicName string) *Topic { n.Lock() t, ok := n.topicMap[topicName] if ok { n.Unlock() return t } deleteCallback := func(t *Topic) { n.DeleteExistingTopic(t.name) } t = NewTopic(topicName, &context{n}, deleteCallback) n.topicMap[topicName] = t n.logf("TOPIC(%s): created", t.name) // release our global nsqd lock, and switch to a more granular topic lock while we init our // channels from lookupd. This blocks concurrent PutMessages to this topic. t.Lock() n.Unlock() // if using lookupd, make a blocking call to get the topics, and immediately create them. // this makes sure that any message received is buffered to the right channels lookupdHTTPAddrs := n.lookupdHTTPAddrs() if len(lookupdHTTPAddrs) > 0 { channelNames, _ := lookupd.GetLookupdTopicChannels(t.name, lookupdHTTPAddrs) for _, channelName := range channelNames { if strings.HasSuffix(channelName, "#ephemeral") { // we don't want to pre-create ephemeral channels // because there isn't a client connected continue } t.getOrCreateChannel(channelName) } } t.Unlock() // NOTE: I would prefer for this to only happen in topic.GetChannel() but we're special // casing the code above so that we can control the locks such that it is impossible // for a message to be written to a (new) topic while we're looking up channels // from lookupd... // // update messagePump state select { case t.channelUpdateChan <- 1: case <-t.exitChan: } return t }
func (s *httpServer) lookupHandler(w http.ResponseWriter, req *http.Request) { reqParams, err := http_api.NewReqParams(req) if err != nil { s.ctx.nsqadmin.logf("ERROR: failed to parse request params - %s", err) http.Error(w, "INVALID_REQUEST", 500) return } channels := make(map[string][]string) allTopics, _ := lookupd.GetLookupdTopics(s.ctx.nsqadmin.opts.NSQLookupdHTTPAddresses) for _, topicName := range allTopics { var producers []string producers, _ = lookupd.GetLookupdTopicProducers(topicName, s.ctx.nsqadmin.opts.NSQLookupdHTTPAddresses) if len(producers) == 0 { topicChannels, _ := lookupd.GetLookupdTopicChannels(topicName, s.ctx.nsqadmin.opts.NSQLookupdHTTPAddresses) channels[topicName] = topicChannels } } p := struct { Title string GraphOptions *GraphOptions TopicMap map[string][]string Lookupd []string Version string }{ Title: "NSQ Lookup", GraphOptions: NewGraphOptions(w, req, reqParams, s.ctx), TopicMap: channels, Lookupd: s.ctx.nsqadmin.opts.NSQLookupdHTTPAddresses, Version: version.Binary, } err = templates.T.ExecuteTemplate(w, "lookup.html", p) if err != nil { s.ctx.nsqadmin.logf("Template Error %s", err) http.Error(w, "Template Error", 500) } }