func TestInactiveNodes(t *testing.T) { opts := NewOptions() opts.Logger = test.NewTestLogger(t) opts.InactiveProducerTimeout = 200 * time.Millisecond tcpAddr, httpAddr, nsqlookupd := mustStartLookupd(opts) defer nsqlookupd.Exit() lookupdHTTPAddrs := []string{fmt.Sprintf("%s", httpAddr)} topicName := "inactive_nodes" conn := mustConnectLookupd(t, tcpAddr) defer conn.Close() identify(t, conn) nsq.Register(topicName, "channel1").WriteTo(conn) _, err := nsq.ReadResponse(conn) test.Nil(t, err) ci := clusterinfo.New(nil, http_api.NewClient(nil, ConnectTimeout, RequestTimeout)) producers, _ := ci.GetLookupdProducers(lookupdHTTPAddrs) test.Equal(t, 1, len(producers)) test.Equal(t, 1, len(producers[0].Topics)) test.Equal(t, topicName, producers[0].Topics[0].Topic) test.Equal(t, false, producers[0].Topics[0].Tombstoned) time.Sleep(250 * time.Millisecond) producers, _ = ci.GetLookupdProducers(lookupdHTTPAddrs) test.Equal(t, 0, len(producers)) }
func TestInactiveNodes(t *testing.T) { opts := NewOptions() opts.Logger = newTestLogger(t) opts.InactiveProducerTimeout = 200 * time.Millisecond tcpAddr, httpAddr, nsqlookupd := mustStartLookupd(opts) defer nsqlookupd.Exit() lookupdHTTPAddrs := []string{fmt.Sprintf("%s", httpAddr)} topicName := "inactive_nodes" conn := mustConnectLookupd(t, tcpAddr) defer conn.Close() identify(t, conn, "ip.address", 5000, 5555, "fake-version") nsq.Register(topicName, "channel1").WriteTo(conn) _, err := nsq.ReadResponse(conn) equal(t, err, nil) ci := clusterinfo.New(nil, http_api.NewClient(nil)) producers, _ := ci.GetLookupdProducers(lookupdHTTPAddrs) equal(t, len(producers), 1) equal(t, len(producers[0].Topics), 1) equal(t, producers[0].Topics[0].Topic, topicName) equal(t, producers[0].Topics[0].Tombstoned, false) time.Sleep(250 * time.Millisecond) producers, _ = ci.GetLookupdProducers(lookupdHTTPAddrs) equal(t, len(producers), 0) }
func New(opts *Options) *NSQD { n := &NSQD{ flag: flagHealthy, startTime: time.Now(), topicMap: make(map[string]*Topic), idChan: make(chan MessageID, 4096), exitChan: make(chan int), notifyChan: make(chan interface{}), optsNotificationChan: make(chan struct{}, 1), ci: clusterinfo.New(opts.Logger, http_api.NewClient(nil)), } n.swapOpts(opts) if opts.MaxDeflateLevel < 1 || opts.MaxDeflateLevel > 9 { n.logf("FATAL: --max-deflate-level must be [1,9]") os.Exit(1) } if opts.ID < 0 || opts.ID >= 1024 { n.logf("FATAL: --worker-id must be [0,1024)") os.Exit(1) } if opts.StatsdPrefix != "" { _, port, err := net.SplitHostPort(opts.HTTPAddress) if err != nil { n.logf("ERROR: failed to parse HTTP address (%s) - %s", opts.HTTPAddress, err) os.Exit(1) } statsdHostKey := statsd.HostKey(net.JoinHostPort(opts.BroadcastAddress, port)) prefixWithHost := strings.Replace(opts.StatsdPrefix, "%s", statsdHostKey, -1) if prefixWithHost[len(prefixWithHost)-1] != '.' { prefixWithHost += "." } opts.StatsdPrefix = prefixWithHost } if opts.TLSClientAuthPolicy != "" && opts.TLSRequired == TLSNotRequired { opts.TLSRequired = TLSRequired } tlsConfig, err := buildTLSConfig(opts) if err != nil { n.logf("FATAL: failed to build TLS config - %s", err) os.Exit(1) } if tlsConfig == nil && opts.TLSRequired != TLSNotRequired { n.logf("FATAL: cannot require TLS client connections without TLS key and cert") os.Exit(1) } n.tlsConfig = tlsConfig n.logf(version.String("nsqd")) n.logf("ID: %d", opts.ID) return n }
func NewHTTPServer(ctx *Context) *httpServer { log := http_api.Log(ctx.nsqadmin.getOpts().Logger) client := http_api.NewClient(ctx.nsqadmin.httpClientTLSConfig, ctx.nsqadmin.getOpts().HTTPClientConnectTimeout, ctx.nsqadmin.getOpts().HTTPClientRequestTimeout) router := httprouter.New() router.HandleMethodNotAllowed = true router.PanicHandler = http_api.LogPanicHandler(ctx.nsqadmin.getOpts().Logger) router.NotFound = http_api.LogNotFoundHandler(ctx.nsqadmin.getOpts().Logger) router.MethodNotAllowed = http_api.LogMethodNotAllowedHandler(ctx.nsqadmin.getOpts().Logger) s := &httpServer{ ctx: ctx, router: router, client: client, ci: clusterinfo.New(ctx.nsqadmin.getOpts().Logger, client), } router.Handle("GET", "/ping", http_api.Decorate(s.pingHandler, log, http_api.PlainText)) router.Handle("GET", "/", http_api.Decorate(s.indexHandler, log)) router.Handle("GET", "/topics", http_api.Decorate(s.indexHandler, log)) router.Handle("GET", "/topics/:topic", http_api.Decorate(s.indexHandler, log)) router.Handle("GET", "/topics/:topic/:channel", http_api.Decorate(s.indexHandler, log)) router.Handle("GET", "/nodes", http_api.Decorate(s.indexHandler, log)) router.Handle("GET", "/nodes/:node", http_api.Decorate(s.indexHandler, log)) router.Handle("GET", "/counter", http_api.Decorate(s.indexHandler, log)) router.Handle("GET", "/lookup", http_api.Decorate(s.indexHandler, log)) router.Handle("GET", "/static/:asset", http_api.Decorate(s.staticAssetHandler, log, http_api.PlainText)) router.Handle("GET", "/fonts/:asset", http_api.Decorate(s.staticAssetHandler, log, http_api.PlainText)) if s.ctx.nsqadmin.getOpts().ProxyGraphite { proxy := NewSingleHostReverseProxy(ctx.nsqadmin.graphiteURL, ctx.nsqadmin.getOpts().HTTPClientConnectTimeout, ctx.nsqadmin.getOpts().HTTPClientRequestTimeout) router.Handler("GET", "/render", proxy) } // v1 endpoints router.Handle("GET", "/api/topics", http_api.Decorate(s.topicsHandler, log, http_api.V1)) router.Handle("GET", "/api/topics/:topic", http_api.Decorate(s.topicHandler, log, http_api.V1)) router.Handle("GET", "/api/topics/:topic/:channel", http_api.Decorate(s.channelHandler, log, http_api.V1)) router.Handle("GET", "/api/nodes", http_api.Decorate(s.nodesHandler, log, http_api.V1)) router.Handle("GET", "/api/nodes/:node", http_api.Decorate(s.nodeHandler, log, http_api.V1)) router.Handle("POST", "/api/topics", http_api.Decorate(s.createTopicChannelHandler, log, http_api.V1)) router.Handle("POST", "/api/topics/:topic", http_api.Decorate(s.topicActionHandler, log, http_api.V1)) router.Handle("POST", "/api/topics/:topic/:channel", http_api.Decorate(s.channelActionHandler, log, http_api.V1)) router.Handle("DELETE", "/api/nodes/:node", http_api.Decorate(s.tombstoneNodeForTopicHandler, log, http_api.V1)) router.Handle("DELETE", "/api/topics/:topic", http_api.Decorate(s.deleteTopicHandler, log, http_api.V1)) router.Handle("DELETE", "/api/topics/:topic/:channel", http_api.Decorate(s.deleteChannelHandler, log, http_api.V1)) router.Handle("GET", "/api/counter", http_api.Decorate(s.counterHandler, log, http_api.V1)) router.Handle("GET", "/api/graphite", http_api.Decorate(s.graphiteHandler, log, http_api.V1)) router.Handle("GET", "/config/:opt", http_api.Decorate(s.doConfig, log, http_api.V1)) router.Handle("PUT", "/config/:opt", http_api.Decorate(s.doConfig, log, http_api.V1)) return s }
func (t *TopicDiscoverer) syncTopics(addrs []string, pattern string) { newTopics, err := clusterinfo.New(nil, http_api.NewClient(nil)).GetLookupdTopics(addrs) if err != nil { log.Printf("ERROR: could not retrieve topic list: %s", err) } for _, topic := range newTopics { if _, ok := t.topics[topic]; !ok { if !t.allowTopicName(pattern, topic) { log.Println("Skipping topic ", topic, "as it didn't match required pattern:", pattern) continue } logger, err := newConsumerFileLogger(topic, t.cfg) if err != nil { log.Printf("ERROR: couldn't create logger for new topic %s: %s", topic, err) continue } t.topics[topic] = logger go t.startTopicRouter(logger) } } }
func TestTombstonedNodes(t *testing.T) { opts := NewOptions() opts.Logger = newTestLogger(t) tcpAddr, httpAddr, nsqlookupd := mustStartLookupd(opts) defer nsqlookupd.Exit() lookupdHTTPAddrs := []string{fmt.Sprintf("%s", httpAddr)} topicName := "inactive_nodes" conn := mustConnectLookupd(t, tcpAddr) defer conn.Close() identify(t, conn, "ip.address", 5000, 5555, "fake-version") nsq.Register(topicName, "channel1").WriteTo(conn) _, err := nsq.ReadResponse(conn) equal(t, err, nil) ci := clusterinfo.New(nil, http_api.NewClient(nil)) producers, _ := ci.GetLookupdProducers(lookupdHTTPAddrs) equal(t, len(producers), 1) equal(t, len(producers[0].Topics), 1) equal(t, producers[0].Topics[0].Topic, topicName) equal(t, producers[0].Topics[0].Tombstoned, false) endpoint := fmt.Sprintf("http://%s/topic/tombstone?topic=%s&node=%s", httpAddr, topicName, "ip.address:5555") err = http_api.NewClient(nil).POSTV1(endpoint) equal(t, err, nil) producers, _ = ci.GetLookupdProducers(lookupdHTTPAddrs) equal(t, len(producers), 1) equal(t, len(producers[0].Topics), 1) equal(t, producers[0].Topics[0].Topic, topicName) equal(t, producers[0].Topics[0].Tombstoned, true) }
func TestTombstonedNodes(t *testing.T) { opts := NewOptions() opts.Logger = test.NewTestLogger(t) tcpAddr, httpAddr, nsqlookupd := mustStartLookupd(opts) defer nsqlookupd.Exit() lookupdHTTPAddrs := []string{fmt.Sprintf("%s", httpAddr)} topicName := "inactive_nodes" conn := mustConnectLookupd(t, tcpAddr) defer conn.Close() identify(t, conn) nsq.Register(topicName, "channel1").WriteTo(conn) _, err := nsq.ReadResponse(conn) test.Nil(t, err) ci := clusterinfo.New(nil, http_api.NewClient(nil, ConnectTimeout, RequestTimeout)) producers, _ := ci.GetLookupdProducers(lookupdHTTPAddrs) test.Equal(t, 1, len(producers)) test.Equal(t, 1, len(producers[0].Topics)) test.Equal(t, topicName, producers[0].Topics[0].Topic) test.Equal(t, false, producers[0].Topics[0].Tombstoned) endpoint := fmt.Sprintf("http://%s/topic/tombstone?topic=%s&node=%s:%d", httpAddr, topicName, HostAddr, HTTPPort) err = http_api.NewClient(nil, ConnectTimeout, RequestTimeout).POSTV1(endpoint) test.Nil(t, err) producers, _ = ci.GetLookupdProducers(lookupdHTTPAddrs) test.Equal(t, 1, len(producers)) test.Equal(t, 1, len(producers[0].Topics)) test.Equal(t, topicName, producers[0].Topics[0].Topic) test.Equal(t, true, producers[0].Topics[0].Tombstoned) }
func main() { cfg := nsq.NewConfig() // TODO: remove, deprecated flag.Var(&nsq.ConfigFlag{cfg}, "reader-opt", "(deprecated) use --consumer-opt") flag.Var(&nsq.ConfigFlag{cfg}, "consumer-opt", "option to passthrough to nsq.Consumer (may be given multiple times, http://godoc.org/github.com/nsqio/go-nsq#Config)") flag.Parse() if *showVersion { fmt.Printf("nsq_to_file v%s\n", version.Binary) return } if *channel == "" { log.Fatal("--channel is required") } var topicsFromNSQLookupd bool if len(nsqdTCPAddrs) == 0 && len(lookupdHTTPAddrs) == 0 { log.Fatal("--nsqd-tcp-address or --lookupd-http-address required.") } if len(nsqdTCPAddrs) != 0 && len(lookupdHTTPAddrs) != 0 { log.Fatal("use --nsqd-tcp-address or --lookupd-http-address not both") } if *gzipLevel < 1 || *gzipLevel > 9 { log.Fatalf("invalid --gzip-level value (%d), should be 1-9", *gzipLevel) } // TODO: remove, deprecated if hasArg("gzip-compression") { log.Printf("WARNING: --gzip-compression is deprecated in favor of --gzip-level") switch *gzipCompression { case 1: *gzipLevel = gzip.BestSpeed case 2: *gzipLevel = gzip.BestCompression case 3: *gzipLevel = gzip.DefaultCompression default: log.Fatalf("invalid --gzip-compression value (%d), should be 1,2,3", *gzipCompression) } } cfg.UserAgent = fmt.Sprintf("nsq_to_file/%s go-nsq/%s", version.Binary, nsq.VERSION) cfg.MaxInFlight = *maxInFlight discoverer := newTopicDiscoverer(cfg) signal.Notify(discoverer.hupChan, syscall.SIGHUP) signal.Notify(discoverer.termChan, syscall.SIGINT, syscall.SIGTERM) if len(topics) < 1 { if len(lookupdHTTPAddrs) < 1 { log.Fatal("use --topic to list at least one topic to subscribe to or specify at least one --lookupd-http-address to subscribe to all its topics") } topicsFromNSQLookupd = true var err error topics, err = clusterinfo.New(nil, http_api.NewClient(nil)).GetLookupdTopics(lookupdHTTPAddrs) if err != nil { log.Fatalf("ERROR: could not retrieve topic list: %s", err) } } for _, topic := range topics { if !discoverer.allowTopicName(*topicPattern, topic) { log.Println("Skipping topic", topic, "as it didn't match required pattern:", *topicPattern) continue } logger, err := newConsumerFileLogger(topic, cfg) if err != nil { log.Fatalf("ERROR: couldn't create logger for topic %s: %s", topic, err) } discoverer.topics[topic] = logger go discoverer.startTopicRouter(logger) } discoverer.watch(lookupdHTTPAddrs, topicsFromNSQLookupd, *topicPattern) }
func statLoop(interval time.Duration, topic string, channel string, nsqdTCPAddrs []string, lookupdHTTPAddrs []string) { ci := clusterinfo.New(nil, http_api.NewClient(nil)) var o *clusterinfo.ChannelStats for i := 0; !countNum.isSet || countNum.value >= i; i++ { var producers clusterinfo.Producers var err error if len(lookupdHTTPAddrs) != 0 { producers, err = ci.GetLookupdTopicProducers(topic, lookupdHTTPAddrs) } else { producers, err = ci.GetNSQDTopicProducers(topic, nsqdHTTPAddrs) } if err != nil { log.Fatalf("ERROR: failed to get topic producers - %s", err) } _, allChannelStats, err := ci.GetNSQDStats(producers, topic) if err != nil { log.Fatalf("ERROR: failed to get nsqd stats - %s", err) } c, ok := allChannelStats[channel] if !ok { log.Fatalf("ERROR: failed to find channel(%s) in stats metadata for topic(%s)", channel, topic) } if i%25 == 0 { fmt.Printf("%s+%s+%s\n", "------rate------", "----------------depth----------------", "--------------metadata---------------") fmt.Printf("%7s %7s | %7s %7s %7s %5s %5s | %7s %7s %12s %7s\n", "ingress", "egress", "total", "mem", "disk", "inflt", "def", "req", "t-o", "msgs", "clients") } if o == nil { o = c time.Sleep(interval) continue } // TODO: paused fmt.Printf("%7d %7d | %7d %7d %7d %5d %5d | %7d %7d %12d %7d\n", int64(float64(c.MessageCount-o.MessageCount)/interval.Seconds()), int64(float64(c.MessageCount-o.MessageCount-(c.Depth-o.Depth))/interval.Seconds()), c.Depth, c.MemoryDepth, c.BackendDepth, c.InFlightCount, c.DeferredCount, c.RequeueCount, c.TimeoutCount, c.MessageCount, c.ClientCount) o = c time.Sleep(interval) } os.Exit(0) }
func New(opts *Options) *NSQD { dataPath := opts.DataPath if opts.DataPath == "" { cwd, _ := os.Getwd() dataPath = cwd } n := &NSQD{ startTime: time.Now(), topicMap: make(map[string]*Topic), idChan: make(chan MessageID, 4096), exitChan: make(chan int), notifyChan: make(chan interface{}), optsNotificationChan: make(chan struct{}, 1), ci: clusterinfo.New(opts.Logger, http_api.NewClient(nil, opts.HTTPClientConnectTimeout, opts.HTTPClientRequestTimeout)), dl: dirlock.New(dataPath), } n.swapOpts(opts) n.errValue.Store(errStore{}) err := n.dl.Lock() if err != nil { n.logf("FATAL: --data-path=%s in use (possibly by another instance of nsqd)", dataPath) os.Exit(1) } if opts.MaxDeflateLevel < 1 || opts.MaxDeflateLevel > 9 { n.logf("FATAL: --max-deflate-level must be [1,9]") os.Exit(1) } if opts.ID < 0 || opts.ID >= 1024 { n.logf("FATAL: --worker-id must be [0,1024)") os.Exit(1) } if opts.StatsdPrefix != "" { var port string _, port, err = net.SplitHostPort(opts.HTTPAddress) if err != nil { n.logf("ERROR: failed to parse HTTP address (%s) - %s", opts.HTTPAddress, err) os.Exit(1) } statsdHostKey := statsd.HostKey(net.JoinHostPort(opts.BroadcastAddress, port)) prefixWithHost := strings.Replace(opts.StatsdPrefix, "%s", statsdHostKey, -1) if prefixWithHost[len(prefixWithHost)-1] != '.' { prefixWithHost += "." } opts.StatsdPrefix = prefixWithHost } if opts.TLSClientAuthPolicy != "" && opts.TLSRequired == TLSNotRequired { opts.TLSRequired = TLSRequired } tlsConfig, err := buildTLSConfig(opts) if err != nil { n.logf("FATAL: failed to build TLS config - %s", err) os.Exit(1) } if tlsConfig == nil && opts.TLSRequired != TLSNotRequired { n.logf("FATAL: cannot require TLS client connections without TLS key and cert") os.Exit(1) } n.tlsConfig = tlsConfig n.logf(version.String("nsqd")) n.logf("ID: %d", opts.ID) return n }
func New(opts *Options) *NSQD { //缓存消息的磁盘路径 dataPath := opts.DataPath if opts.DataPath == "" { cwd, _ := os.Getwd() dataPath = cwd } n := &NSQD{ startTime: time.Now(), topicMap: make(map[string]*Topic), idChan: make(chan MessageID, 4096), exitChan: make(chan int), notifyChan: make(chan interface{}), optsNotificationChan: make(chan struct{}, 1), //WEB接听 ci: clusterinfo.New(opts.Logger, http_api.NewClient(nil)), dl: dirlock.New(dataPath), } // 设置原子操作对象(配置信息) --???--是否还可以正常方式读写 (sync/atomic) (原子操作的效率要比锁来的高) n.swapOpts(opts) n.errValue.Store(errStore{}) //阻止数据库文件的外来操作 err := n.dl.Lock() if err != nil { n.logf("FATAL: --data-path=%s in use (possibly by another instance of nsqd)", dataPath) os.Exit(1) } //-max-deflate-level=6: 最大的压缩比率等级 if opts.MaxDeflateLevel < 1 || opts.MaxDeflateLevel > 9 { n.logf("FATAL: --max-deflate-level must be [1,9]") os.Exit(1) } //-worker-id=0: 进程的唯一码(默认是主机名的哈希值) if opts.ID < 0 || opts.ID >= 1024 { n.logf("FATAL: --worker-id must be [0,1024)") os.Exit(1) } //-statsd-prefix="nsq.%s": 发送给统计keys 的前缀(%s for host replacement) if opts.StatsdPrefix != "" { //生成显示前缀 var port string //-http-address="0.0.0.0:4151": 为 HTTP 客户端监听 <addr>:<port> _, port, err = net.SplitHostPort(opts.HTTPAddress) if err != nil { n.logf("ERROR: failed to parse HTTP address (%s) - %s", opts.HTTPAddress, err) os.Exit(1) } statsdHostKey := statsd.HostKey(net.JoinHostPort(opts.BroadcastAddress, port)) prefixWithHost := strings.Replace(opts.StatsdPrefix, "%s", statsdHostKey, -1) if prefixWithHost[len(prefixWithHost)-1] != '.' { prefixWithHost += "." } opts.StatsdPrefix = prefixWithHost } //TLS安全文件传输协议 if opts.TLSClientAuthPolicy != "" && opts.TLSRequired == TLSNotRequired { opts.TLSRequired = TLSRequired } tlsConfig, err := buildTLSConfig(opts) if err != nil { n.logf("FATAL: failed to build TLS config - %s", err) os.Exit(1) } if tlsConfig == nil && opts.TLSRequired != TLSNotRequired { n.logf("FATAL: cannot require TLS client connections without TLS key and cert") os.Exit(1) } n.tlsConfig = tlsConfig n.logf(version.String("nsqd")) n.logf("ID: %d", opts.ID) return n }