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  }