github.com/nsqio/nsq@v1.3.0/nsqd/nsqd.go (about)

     1  package nsqd
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"log"
    11  	"math/rand"
    12  	"net"
    13  	"os"
    14  	"path"
    15  	"strings"
    16  	"sync"
    17  	"sync/atomic"
    18  	"time"
    19  
    20  	"github.com/nsqio/nsq/internal/clusterinfo"
    21  	"github.com/nsqio/nsq/internal/dirlock"
    22  	"github.com/nsqio/nsq/internal/http_api"
    23  	"github.com/nsqio/nsq/internal/protocol"
    24  	"github.com/nsqio/nsq/internal/statsd"
    25  	"github.com/nsqio/nsq/internal/util"
    26  	"github.com/nsqio/nsq/internal/version"
    27  )
    28  
    29  const (
    30  	TLSNotRequired = iota
    31  	TLSRequiredExceptHTTP
    32  	TLSRequired
    33  )
    34  
    35  type errStore struct {
    36  	err error
    37  }
    38  
    39  type NSQD struct {
    40  	// 64bit atomic vars need to be first for proper alignment on 32bit platforms
    41  	clientIDSequence int64
    42  
    43  	sync.RWMutex
    44  	ctx context.Context
    45  	// ctxCancel cancels a context that main() is waiting on
    46  	ctxCancel context.CancelFunc
    47  
    48  	opts atomic.Value
    49  
    50  	dl        *dirlock.DirLock
    51  	isLoading int32
    52  	isExiting int32
    53  	errValue  atomic.Value
    54  	startTime time.Time
    55  
    56  	topicMap map[string]*Topic
    57  
    58  	lookupPeers atomic.Value
    59  
    60  	tcpServer       *tcpServer
    61  	tcpListener     net.Listener
    62  	httpListener    net.Listener
    63  	httpsListener   net.Listener
    64  	tlsConfig       *tls.Config
    65  	clientTLSConfig *tls.Config
    66  
    67  	poolSize int
    68  
    69  	notifyChan           chan interface{}
    70  	optsNotificationChan chan struct{}
    71  	exitChan             chan int
    72  	waitGroup            util.WaitGroupWrapper
    73  
    74  	ci *clusterinfo.ClusterInfo
    75  }
    76  
    77  func New(opts *Options) (*NSQD, error) {
    78  	var err error
    79  
    80  	dataPath := opts.DataPath
    81  	if opts.DataPath == "" {
    82  		cwd, _ := os.Getwd()
    83  		dataPath = cwd
    84  	}
    85  	if opts.Logger == nil {
    86  		opts.Logger = log.New(os.Stderr, opts.LogPrefix, log.Ldate|log.Ltime|log.Lmicroseconds)
    87  	}
    88  
    89  	n := &NSQD{
    90  		startTime:            time.Now(),
    91  		topicMap:             make(map[string]*Topic),
    92  		exitChan:             make(chan int),
    93  		notifyChan:           make(chan interface{}),
    94  		optsNotificationChan: make(chan struct{}, 1),
    95  		dl:                   dirlock.New(dataPath),
    96  	}
    97  	n.ctx, n.ctxCancel = context.WithCancel(context.Background())
    98  	httpcli := http_api.NewClient(nil, opts.HTTPClientConnectTimeout, opts.HTTPClientRequestTimeout)
    99  	n.ci = clusterinfo.New(n.logf, httpcli)
   100  
   101  	n.lookupPeers.Store([]*lookupPeer{})
   102  
   103  	n.swapOpts(opts)
   104  	n.errValue.Store(errStore{})
   105  
   106  	err = n.dl.Lock()
   107  	if err != nil {
   108  		return nil, fmt.Errorf("failed to lock data-path: %v", err)
   109  	}
   110  
   111  	if opts.MaxDeflateLevel < 1 || opts.MaxDeflateLevel > 9 {
   112  		return nil, errors.New("--max-deflate-level must be [1,9]")
   113  	}
   114  
   115  	if opts.ID < 0 || opts.ID >= 1024 {
   116  		return nil, errors.New("--node-id must be [0,1024)")
   117  	}
   118  
   119  	if opts.TLSClientAuthPolicy != "" && opts.TLSRequired == TLSNotRequired {
   120  		opts.TLSRequired = TLSRequired
   121  	}
   122  
   123  	tlsConfig, err := buildTLSConfig(opts)
   124  	if err != nil {
   125  		return nil, fmt.Errorf("failed to build TLS config - %s", err)
   126  	}
   127  	if tlsConfig == nil && opts.TLSRequired != TLSNotRequired {
   128  		return nil, errors.New("cannot require TLS client connections without TLS key and cert")
   129  	}
   130  	n.tlsConfig = tlsConfig
   131  
   132  	clientTLSConfig, err := buildClientTLSConfig(opts)
   133  	if err != nil {
   134  		return nil, fmt.Errorf("failed to build client TLS config - %s", err)
   135  	}
   136  	n.clientTLSConfig = clientTLSConfig
   137  
   138  	for _, v := range opts.E2EProcessingLatencyPercentiles {
   139  		if v <= 0 || v > 1 {
   140  			return nil, fmt.Errorf("invalid E2E processing latency percentile: %v", v)
   141  		}
   142  	}
   143  
   144  	n.logf(LOG_INFO, version.String("nsqd"))
   145  	n.logf(LOG_INFO, "ID: %d", opts.ID)
   146  
   147  	n.tcpServer = &tcpServer{nsqd: n}
   148  	n.tcpListener, err = net.Listen(util.TypeOfAddr(opts.TCPAddress), opts.TCPAddress)
   149  	if err != nil {
   150  		return nil, fmt.Errorf("listen (%s) failed - %s", opts.TCPAddress, err)
   151  	}
   152  	if opts.HTTPAddress != "" {
   153  		n.httpListener, err = net.Listen(util.TypeOfAddr(opts.HTTPAddress), opts.HTTPAddress)
   154  		if err != nil {
   155  			return nil, fmt.Errorf("listen (%s) failed - %s", opts.HTTPAddress, err)
   156  		}
   157  	}
   158  	if n.tlsConfig != nil && opts.HTTPSAddress != "" {
   159  		n.httpsListener, err = tls.Listen("tcp", opts.HTTPSAddress, n.tlsConfig)
   160  		if err != nil {
   161  			return nil, fmt.Errorf("listen (%s) failed - %s", opts.HTTPSAddress, err)
   162  		}
   163  	}
   164  	if opts.BroadcastHTTPPort == 0 {
   165  		tcpAddr, ok := n.RealHTTPAddr().(*net.TCPAddr)
   166  		if ok {
   167  			opts.BroadcastHTTPPort = tcpAddr.Port
   168  		}
   169  	}
   170  
   171  	if opts.BroadcastTCPPort == 0 {
   172  		tcpAddr, ok := n.RealTCPAddr().(*net.TCPAddr)
   173  		if ok {
   174  			opts.BroadcastTCPPort = tcpAddr.Port
   175  		}
   176  	}
   177  
   178  	if opts.StatsdPrefix != "" {
   179  		var port string = fmt.Sprint(opts.BroadcastHTTPPort)
   180  		statsdHostKey := statsd.HostKey(net.JoinHostPort(opts.BroadcastAddress, port))
   181  		prefixWithHost := strings.Replace(opts.StatsdPrefix, "%s", statsdHostKey, -1)
   182  		if prefixWithHost[len(prefixWithHost)-1] != '.' {
   183  			prefixWithHost += "."
   184  		}
   185  		opts.StatsdPrefix = prefixWithHost
   186  	}
   187  
   188  	return n, nil
   189  }
   190  
   191  func (n *NSQD) getOpts() *Options {
   192  	return n.opts.Load().(*Options)
   193  }
   194  
   195  func (n *NSQD) swapOpts(opts *Options) {
   196  	n.opts.Store(opts)
   197  }
   198  
   199  func (n *NSQD) triggerOptsNotification() {
   200  	select {
   201  	case n.optsNotificationChan <- struct{}{}:
   202  	default:
   203  	}
   204  }
   205  
   206  func (n *NSQD) RealTCPAddr() net.Addr {
   207  	if n.tcpListener == nil {
   208  		return &net.TCPAddr{}
   209  	}
   210  	return n.tcpListener.Addr()
   211  
   212  }
   213  
   214  func (n *NSQD) RealHTTPAddr() net.Addr {
   215  	if n.httpListener == nil {
   216  		return &net.TCPAddr{}
   217  	}
   218  	return n.httpListener.Addr()
   219  }
   220  
   221  func (n *NSQD) RealHTTPSAddr() *net.TCPAddr {
   222  	if n.httpsListener == nil {
   223  		return &net.TCPAddr{}
   224  	}
   225  	return n.httpsListener.Addr().(*net.TCPAddr)
   226  }
   227  
   228  func (n *NSQD) SetHealth(err error) {
   229  	n.errValue.Store(errStore{err: err})
   230  }
   231  
   232  func (n *NSQD) IsHealthy() bool {
   233  	return n.GetError() == nil
   234  }
   235  
   236  func (n *NSQD) GetError() error {
   237  	errValue := n.errValue.Load()
   238  	return errValue.(errStore).err
   239  }
   240  
   241  func (n *NSQD) GetHealth() string {
   242  	err := n.GetError()
   243  	if err != nil {
   244  		return fmt.Sprintf("NOK - %s", err)
   245  	}
   246  	return "OK"
   247  }
   248  
   249  func (n *NSQD) GetStartTime() time.Time {
   250  	return n.startTime
   251  }
   252  
   253  func (n *NSQD) Main() error {
   254  	exitCh := make(chan error)
   255  	var once sync.Once
   256  	exitFunc := func(err error) {
   257  		once.Do(func() {
   258  			if err != nil {
   259  				n.logf(LOG_FATAL, "%s", err)
   260  			}
   261  			exitCh <- err
   262  		})
   263  	}
   264  
   265  	n.waitGroup.Wrap(func() {
   266  		exitFunc(protocol.TCPServer(n.tcpListener, n.tcpServer, n.logf))
   267  	})
   268  	if n.httpListener != nil {
   269  		httpServer := newHTTPServer(n, false, n.getOpts().TLSRequired == TLSRequired)
   270  		n.waitGroup.Wrap(func() {
   271  			exitFunc(http_api.Serve(n.httpListener, httpServer, "HTTP", n.logf))
   272  		})
   273  	}
   274  	if n.httpsListener != nil {
   275  		httpsServer := newHTTPServer(n, true, true)
   276  		n.waitGroup.Wrap(func() {
   277  			exitFunc(http_api.Serve(n.httpsListener, httpsServer, "HTTPS", n.logf))
   278  		})
   279  	}
   280  
   281  	n.waitGroup.Wrap(n.queueScanLoop)
   282  	n.waitGroup.Wrap(n.lookupLoop)
   283  	if n.getOpts().StatsdAddress != "" {
   284  		n.waitGroup.Wrap(n.statsdLoop)
   285  	}
   286  
   287  	err := <-exitCh
   288  	return err
   289  }
   290  
   291  // Metadata is the collection of persistent information about the current NSQD.
   292  type Metadata struct {
   293  	Topics  []TopicMetadata `json:"topics"`
   294  	Version string          `json:"version"`
   295  }
   296  
   297  // TopicMetadata is the collection of persistent information about a topic.
   298  type TopicMetadata struct {
   299  	Name     string            `json:"name"`
   300  	Paused   bool              `json:"paused"`
   301  	Channels []ChannelMetadata `json:"channels"`
   302  }
   303  
   304  // ChannelMetadata is the collection of persistent information about a channel.
   305  type ChannelMetadata struct {
   306  	Name   string `json:"name"`
   307  	Paused bool   `json:"paused"`
   308  }
   309  
   310  func newMetadataFile(opts *Options) string {
   311  	return path.Join(opts.DataPath, "nsqd.dat")
   312  }
   313  
   314  func readOrEmpty(fn string) ([]byte, error) {
   315  	data, err := os.ReadFile(fn)
   316  	if err != nil {
   317  		if !os.IsNotExist(err) {
   318  			return nil, fmt.Errorf("failed to read metadata from %s - %s", fn, err)
   319  		}
   320  	}
   321  	return data, nil
   322  }
   323  
   324  func writeSyncFile(fn string, data []byte) error {
   325  	f, err := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
   326  	if err != nil {
   327  		return err
   328  	}
   329  
   330  	_, err = f.Write(data)
   331  	if err == nil {
   332  		err = f.Sync()
   333  	}
   334  	f.Close()
   335  	return err
   336  }
   337  
   338  func (n *NSQD) LoadMetadata() error {
   339  	atomic.StoreInt32(&n.isLoading, 1)
   340  	defer atomic.StoreInt32(&n.isLoading, 0)
   341  
   342  	fn := newMetadataFile(n.getOpts())
   343  
   344  	data, err := readOrEmpty(fn)
   345  	if err != nil {
   346  		return err
   347  	}
   348  	if data == nil {
   349  		return nil // fresh start
   350  	}
   351  
   352  	var m Metadata
   353  	err = json.Unmarshal(data, &m)
   354  	if err != nil {
   355  		return fmt.Errorf("failed to parse metadata in %s - %s", fn, err)
   356  	}
   357  
   358  	for _, t := range m.Topics {
   359  		if !protocol.IsValidTopicName(t.Name) {
   360  			n.logf(LOG_WARN, "skipping creation of invalid topic %s", t.Name)
   361  			continue
   362  		}
   363  		topic := n.GetTopic(t.Name)
   364  		if t.Paused {
   365  			topic.Pause()
   366  		}
   367  		for _, c := range t.Channels {
   368  			if !protocol.IsValidChannelName(c.Name) {
   369  				n.logf(LOG_WARN, "skipping creation of invalid channel %s", c.Name)
   370  				continue
   371  			}
   372  			channel := topic.GetChannel(c.Name)
   373  			if c.Paused {
   374  				channel.Pause()
   375  			}
   376  		}
   377  		topic.Start()
   378  	}
   379  	return nil
   380  }
   381  
   382  // GetMetadata retrieves the current topic and channel set of the NSQ daemon. If
   383  // the ephemeral flag is set, ephemeral topics are also returned even though these
   384  // are not saved to disk.
   385  func (n *NSQD) GetMetadata(ephemeral bool) *Metadata {
   386  	meta := &Metadata{
   387  		Version: version.Binary,
   388  	}
   389  	for _, topic := range n.topicMap {
   390  		if topic.ephemeral && !ephemeral {
   391  			continue
   392  		}
   393  		topicData := TopicMetadata{
   394  			Name:   topic.name,
   395  			Paused: topic.IsPaused(),
   396  		}
   397  		topic.Lock()
   398  		for _, channel := range topic.channelMap {
   399  			if channel.ephemeral {
   400  				continue
   401  			}
   402  			topicData.Channels = append(topicData.Channels, ChannelMetadata{
   403  				Name:   channel.name,
   404  				Paused: channel.IsPaused(),
   405  			})
   406  		}
   407  		topic.Unlock()
   408  		meta.Topics = append(meta.Topics, topicData)
   409  	}
   410  	return meta
   411  }
   412  
   413  func (n *NSQD) PersistMetadata() error {
   414  	// persist metadata about what topics/channels we have, across restarts
   415  	fileName := newMetadataFile(n.getOpts())
   416  
   417  	n.logf(LOG_INFO, "NSQ: persisting topic/channel metadata to %s", fileName)
   418  
   419  	data, err := json.Marshal(n.GetMetadata(false))
   420  	if err != nil {
   421  		return err
   422  	}
   423  	tmpFileName := fmt.Sprintf("%s.%d.tmp", fileName, rand.Int())
   424  
   425  	err = writeSyncFile(tmpFileName, data)
   426  	if err != nil {
   427  		return err
   428  	}
   429  	err = os.Rename(tmpFileName, fileName)
   430  	if err != nil {
   431  		return err
   432  	}
   433  	// technically should fsync DataPath here
   434  
   435  	return nil
   436  }
   437  
   438  func (n *NSQD) Exit() {
   439  	if !atomic.CompareAndSwapInt32(&n.isExiting, 0, 1) {
   440  		// avoid double call
   441  		return
   442  	}
   443  	if n.tcpListener != nil {
   444  		n.tcpListener.Close()
   445  	}
   446  
   447  	if n.tcpServer != nil {
   448  		n.tcpServer.Close()
   449  	}
   450  
   451  	if n.httpListener != nil {
   452  		n.httpListener.Close()
   453  	}
   454  
   455  	if n.httpsListener != nil {
   456  		n.httpsListener.Close()
   457  	}
   458  
   459  	n.Lock()
   460  	err := n.PersistMetadata()
   461  	if err != nil {
   462  		n.logf(LOG_ERROR, "failed to persist metadata - %s", err)
   463  	}
   464  	n.logf(LOG_INFO, "NSQ: closing topics")
   465  	for _, topic := range n.topicMap {
   466  		topic.Close()
   467  	}
   468  	n.Unlock()
   469  
   470  	n.logf(LOG_INFO, "NSQ: stopping subsystems")
   471  	close(n.exitChan)
   472  	n.waitGroup.Wait()
   473  	n.dl.Unlock()
   474  	n.logf(LOG_INFO, "NSQ: bye")
   475  	n.ctxCancel()
   476  }
   477  
   478  // GetTopic performs a thread safe operation
   479  // to return a pointer to a Topic object (potentially new)
   480  func (n *NSQD) GetTopic(topicName string) *Topic {
   481  	// most likely we already have this topic, so try read lock first
   482  	n.RLock()
   483  	t, ok := n.topicMap[topicName]
   484  	n.RUnlock()
   485  	if ok {
   486  		return t
   487  	}
   488  
   489  	n.Lock()
   490  
   491  	t, ok = n.topicMap[topicName]
   492  	if ok {
   493  		n.Unlock()
   494  		return t
   495  	}
   496  	deleteCallback := func(t *Topic) {
   497  		n.DeleteExistingTopic(t.name)
   498  	}
   499  	t = NewTopic(topicName, n, deleteCallback)
   500  	n.topicMap[topicName] = t
   501  
   502  	n.Unlock()
   503  
   504  	n.logf(LOG_INFO, "TOPIC(%s): created", t.name)
   505  	// topic is created but messagePump not yet started
   506  
   507  	// if this topic was created while loading metadata at startup don't do any further initialization
   508  	// (topic will be "started" after loading completes)
   509  	if atomic.LoadInt32(&n.isLoading) == 1 {
   510  		return t
   511  	}
   512  
   513  	// if using lookupd, make a blocking call to get channels and immediately create them
   514  	// to ensure that all channels receive published messages
   515  	lookupdHTTPAddrs := n.lookupdHTTPAddrs()
   516  	if len(lookupdHTTPAddrs) > 0 {
   517  		channelNames, err := n.ci.GetLookupdTopicChannels(t.name, lookupdHTTPAddrs)
   518  		if err != nil {
   519  			n.logf(LOG_WARN, "failed to query nsqlookupd for channels to pre-create for topic %s - %s", t.name, err)
   520  		}
   521  		for _, channelName := range channelNames {
   522  			if strings.HasSuffix(channelName, "#ephemeral") {
   523  				continue // do not create ephemeral channel with no consumer client
   524  			}
   525  			t.GetChannel(channelName)
   526  		}
   527  	} else if len(n.getOpts().NSQLookupdTCPAddresses) > 0 {
   528  		n.logf(LOG_ERROR, "no available nsqlookupd to query for channels to pre-create for topic %s", t.name)
   529  	}
   530  
   531  	// now that all channels are added, start topic messagePump
   532  	t.Start()
   533  	return t
   534  }
   535  
   536  // GetExistingTopic gets a topic only if it exists
   537  func (n *NSQD) GetExistingTopic(topicName string) (*Topic, error) {
   538  	n.RLock()
   539  	defer n.RUnlock()
   540  	topic, ok := n.topicMap[topicName]
   541  	if !ok {
   542  		return nil, errors.New("topic does not exist")
   543  	}
   544  	return topic, nil
   545  }
   546  
   547  // DeleteExistingTopic removes a topic only if it exists
   548  func (n *NSQD) DeleteExistingTopic(topicName string) error {
   549  	n.RLock()
   550  	topic, ok := n.topicMap[topicName]
   551  	if !ok {
   552  		n.RUnlock()
   553  		return errors.New("topic does not exist")
   554  	}
   555  	n.RUnlock()
   556  
   557  	// delete empties all channels and the topic itself before closing
   558  	// (so that we dont leave any messages around)
   559  	//
   560  	// we do this before removing the topic from map below (with no lock)
   561  	// so that any incoming writes will error and not create a new topic
   562  	// to enforce ordering
   563  	topic.Delete()
   564  
   565  	n.Lock()
   566  	delete(n.topicMap, topicName)
   567  	n.Unlock()
   568  
   569  	return nil
   570  }
   571  
   572  func (n *NSQD) Notify(v interface{}, persist bool) {
   573  	// since the in-memory metadata is incomplete,
   574  	// should not persist metadata while loading it.
   575  	// nsqd will call `PersistMetadata` it after loading
   576  	loading := atomic.LoadInt32(&n.isLoading) == 1
   577  	n.waitGroup.Wrap(func() {
   578  		// by selecting on exitChan we guarantee that
   579  		// we do not block exit, see issue #123
   580  		select {
   581  		case <-n.exitChan:
   582  		case n.notifyChan <- v:
   583  			if loading || !persist {
   584  				return
   585  			}
   586  			n.Lock()
   587  			err := n.PersistMetadata()
   588  			if err != nil {
   589  				n.logf(LOG_ERROR, "failed to persist metadata - %s", err)
   590  			}
   591  			n.Unlock()
   592  		}
   593  	})
   594  }
   595  
   596  // channels returns a flat slice of all channels in all topics
   597  func (n *NSQD) channels() []*Channel {
   598  	var channels []*Channel
   599  	n.RLock()
   600  	for _, t := range n.topicMap {
   601  		t.RLock()
   602  		for _, c := range t.channelMap {
   603  			channels = append(channels, c)
   604  		}
   605  		t.RUnlock()
   606  	}
   607  	n.RUnlock()
   608  	return channels
   609  }
   610  
   611  // resizePool adjusts the size of the pool of queueScanWorker goroutines
   612  //
   613  //	1 <= pool <= min(num * 0.25, QueueScanWorkerPoolMax)
   614  func (n *NSQD) resizePool(num int, workCh chan *Channel, responseCh chan bool, closeCh chan int) {
   615  	idealPoolSize := int(float64(num) * 0.25)
   616  	if idealPoolSize < 1 {
   617  		idealPoolSize = 1
   618  	} else if idealPoolSize > n.getOpts().QueueScanWorkerPoolMax {
   619  		idealPoolSize = n.getOpts().QueueScanWorkerPoolMax
   620  	}
   621  	for {
   622  		if idealPoolSize == n.poolSize {
   623  			break
   624  		} else if idealPoolSize < n.poolSize {
   625  			// contract
   626  			closeCh <- 1
   627  			n.poolSize--
   628  		} else {
   629  			// expand
   630  			n.waitGroup.Wrap(func() {
   631  				n.queueScanWorker(workCh, responseCh, closeCh)
   632  			})
   633  			n.poolSize++
   634  		}
   635  	}
   636  }
   637  
   638  // queueScanWorker receives work (in the form of a channel) from queueScanLoop
   639  // and processes the deferred and in-flight queues
   640  func (n *NSQD) queueScanWorker(workCh chan *Channel, responseCh chan bool, closeCh chan int) {
   641  	for {
   642  		select {
   643  		case c := <-workCh:
   644  			now := time.Now().UnixNano()
   645  			dirty := false
   646  			if c.processInFlightQueue(now) {
   647  				dirty = true
   648  			}
   649  			if c.processDeferredQueue(now) {
   650  				dirty = true
   651  			}
   652  			responseCh <- dirty
   653  		case <-closeCh:
   654  			return
   655  		}
   656  	}
   657  }
   658  
   659  // queueScanLoop runs in a single goroutine to process in-flight and deferred
   660  // priority queues. It manages a pool of queueScanWorker (configurable max of
   661  // QueueScanWorkerPoolMax (default: 4)) that process channels concurrently.
   662  //
   663  // It copies Redis's probabilistic expiration algorithm: it wakes up every
   664  // QueueScanInterval (default: 100ms) to select a random QueueScanSelectionCount
   665  // (default: 20) channels from a locally cached list (refreshed every
   666  // QueueScanRefreshInterval (default: 5s)).
   667  //
   668  // If either of the queues had work to do the channel is considered "dirty".
   669  //
   670  // If QueueScanDirtyPercent (default: 25%) of the selected channels were dirty,
   671  // the loop continues without sleep.
   672  func (n *NSQD) queueScanLoop() {
   673  	workCh := make(chan *Channel, n.getOpts().QueueScanSelectionCount)
   674  	responseCh := make(chan bool, n.getOpts().QueueScanSelectionCount)
   675  	closeCh := make(chan int)
   676  
   677  	workTicker := time.NewTicker(n.getOpts().QueueScanInterval)
   678  	refreshTicker := time.NewTicker(n.getOpts().QueueScanRefreshInterval)
   679  
   680  	channels := n.channels()
   681  	n.resizePool(len(channels), workCh, responseCh, closeCh)
   682  
   683  	for {
   684  		select {
   685  		case <-workTicker.C:
   686  			if len(channels) == 0 {
   687  				continue
   688  			}
   689  		case <-refreshTicker.C:
   690  			channels = n.channels()
   691  			n.resizePool(len(channels), workCh, responseCh, closeCh)
   692  			continue
   693  		case <-n.exitChan:
   694  			goto exit
   695  		}
   696  
   697  		num := n.getOpts().QueueScanSelectionCount
   698  		if num > len(channels) {
   699  			num = len(channels)
   700  		}
   701  
   702  	loop:
   703  		for _, i := range util.UniqRands(num, len(channels)) {
   704  			workCh <- channels[i]
   705  		}
   706  
   707  		numDirty := 0
   708  		for i := 0; i < num; i++ {
   709  			if <-responseCh {
   710  				numDirty++
   711  			}
   712  		}
   713  
   714  		if float64(numDirty)/float64(num) > n.getOpts().QueueScanDirtyPercent {
   715  			goto loop
   716  		}
   717  	}
   718  
   719  exit:
   720  	n.logf(LOG_INFO, "QUEUESCAN: closing")
   721  	close(closeCh)
   722  	workTicker.Stop()
   723  	refreshTicker.Stop()
   724  }
   725  
   726  func buildTLSConfig(opts *Options) (*tls.Config, error) {
   727  	var tlsConfig *tls.Config
   728  
   729  	if opts.TLSCert == "" && opts.TLSKey == "" {
   730  		return nil, nil
   731  	}
   732  
   733  	tlsClientAuthPolicy := tls.VerifyClientCertIfGiven
   734  
   735  	cert, err := tls.LoadX509KeyPair(opts.TLSCert, opts.TLSKey)
   736  	if err != nil {
   737  		return nil, err
   738  	}
   739  	switch opts.TLSClientAuthPolicy {
   740  	case "require":
   741  		tlsClientAuthPolicy = tls.RequireAnyClientCert
   742  	case "require-verify":
   743  		tlsClientAuthPolicy = tls.RequireAndVerifyClientCert
   744  	default:
   745  		tlsClientAuthPolicy = tls.NoClientCert
   746  	}
   747  
   748  	tlsConfig = &tls.Config{
   749  		Certificates: []tls.Certificate{cert},
   750  		ClientAuth:   tlsClientAuthPolicy,
   751  		MinVersion:   opts.TLSMinVersion,
   752  	}
   753  
   754  	if opts.TLSRootCAFile != "" {
   755  		tlsCertPool := x509.NewCertPool()
   756  		caCertFile, err := os.ReadFile(opts.TLSRootCAFile)
   757  		if err != nil {
   758  			return nil, err
   759  		}
   760  		if !tlsCertPool.AppendCertsFromPEM(caCertFile) {
   761  			return nil, errors.New("failed to append certificate to pool")
   762  		}
   763  		tlsConfig.ClientCAs = tlsCertPool
   764  	}
   765  
   766  	return tlsConfig, nil
   767  }
   768  
   769  func buildClientTLSConfig(opts *Options) (*tls.Config, error) {
   770  	tlsConfig := &tls.Config{
   771  		MinVersion: opts.TLSMinVersion,
   772  	}
   773  
   774  	if opts.TLSRootCAFile != "" {
   775  		tlsCertPool := x509.NewCertPool()
   776  		caCertFile, err := os.ReadFile(opts.TLSRootCAFile)
   777  		if err != nil {
   778  			return nil, err
   779  		}
   780  		if !tlsCertPool.AppendCertsFromPEM(caCertFile) {
   781  			return nil, errors.New("failed to append certificate to pool")
   782  		}
   783  		tlsConfig.RootCAs = tlsCertPool
   784  	}
   785  
   786  	return tlsConfig, nil
   787  }
   788  
   789  func (n *NSQD) IsAuthEnabled() bool {
   790  	return len(n.getOpts().AuthHTTPAddresses) != 0
   791  }
   792  
   793  // Context returns a context that will be canceled when nsqd initiates the shutdown
   794  func (n *NSQD) Context() context.Context {
   795  	return n.ctx
   796  }