NSQ 源碼分析之NSQD 的啓動

1.NSQD的配置(nsqd/options.go)包括如下參數:

type Options struct {
	/*******************基本信息配置*********************/
	//節點ID
	ID        int64       `flag:"node-id" cfg:"id"`
	//日誌等級(可參考nsqd/logger.go)
	LogLevel  lg.LogLevel `flag:"log-level"`
	//日誌前綴
	LogPrefix string      `flag:"log-prefix"`
	Logger    Logger

	/*******************地址配置,包括tcp,http,https等*********************/
	//....
	
	/*******************持久化配置*********************/
	//元數據存儲地址(topic/channel的信息)
	DataPath        string        `flag:"data-path"`
	//每個channel最大消息個數,如果超過就需要寫入磁盤
	MemQueueSize    int64         `flag:"mem-queue-size"`
	//寫入磁盤的文件名格式是 xx-000000001, 該配置表示文件名的最大bit位
	//如果是2,文件格式是 xx-00
	MaxBytesPerFile int64         `flag:"max-bytes-per-file"`
	//緩衝區內容同步到磁盤的時間間隔
	SyncEvery       int64         `flag:"sync-every"`
	//同步超時時間
	SyncTimeout     time.Duration `flag:"sync-timeout"`


	/*******************nsqd.queueScanLoop 函數中使用如下參數*********************/
	//nsqd 掃描延時隊列或者未ack隊列的時間間隔
	QueueScanInterval        time.Duration
	//nsqd 調整工作線程和刷新topic.channel的時間間隔
	QueueScanRefreshInterval time.Duration
	//nsqd 掃描channel的數量
	QueueScanSelectionCount  int `flag:"queue-scan-selection-count"`
	//nsqd 掃描各種隊列的最大工作線程
	QueueScanWorkerPoolMax   int `flag:"queue-scan-worker-pool-max"`
	//nsqd 允許掃描失敗的比例。
	QueueScanDirtyPercent    float64

	/*******************MQ消息和命令相關配置*********************/
	//消息最大爲被ack的時間,超過這個時間會被重新推入隊列,重新消費
	MsgTimeout    time.Duration `flag:"msg-timeout"`
	MaxMsgTimeout time.Duration `flag:"max-msg-timeout"`
	//單個消息的最大字節數
	MaxMsgSize    int64         `flag:"max-msg-size"`
	//批量消息的,最大字節數
	MaxBodySize   int64         `flag:"max-body-size"`
	MaxReqTimeout time.Duration `flag:"max-req-timeout"`
	ClientTimeout time.Duration

	/*******************客戶端相關配置*********************/
	//心跳間隔時間
	MaxHeartbeatInterval   time.Duration `flag:"max-heartbeat-interval"`
	//????
	MaxRdyCount            int64         `flag:"max-rdy-count"`
	MaxOutputBufferSize    int64         `flag:"max-output-buffer-size"`
	//根據最大最小緩存輸出時間,生成一個desiredTimeout
	MaxOutputBufferTimeout time.Duration `flag:"max-output-buffer-timeout"`
	MinOutputBufferTimeout time.Duration `flag:"min-output-buffer-timeout"`
	OutputBufferTimeout    time.Duration `flag:"output-buffer-timeout"`
	//channel的最多消費者數量
	MaxChannelConsumers    int           `flag:"max-channel-consumers"`

	// statsd integration
	StatsdAddress       string        `flag:"statsd-address"`
	StatsdPrefix        string        `flag:"statsd-prefix"`
	StatsdInterval      time.Duration `flag:"statsd-interval"`
	StatsdMemStats      bool          `flag:"statsd-mem-stats"`
	StatsdUDPPacketSize int           `flag:"statsd-udp-packet-size"`

	// e2e message latency
	E2EProcessingLatencyWindowTime  time.Duration `flag:"e2e-processing-latency-window-time"`
	E2EProcessingLatencyPercentiles []float64     `flag:"e2e-processing-latency-percentile" cfg:"e2e_processing_latency_percentiles"`

	// TLS config
	TLSCert             string `flag:"tls-cert"`
	TLSKey              string `flag:"tls-key"`
	TLSClientAuthPolicy string `flag:"tls-client-auth-policy"`
	TLSRootCAFile       string `flag:"tls-root-ca-file"`
	TLSRequired         int    `flag:"tls-required"`
	TLSMinVersion       uint16 `flag:"tls-min-version"`

	// compression
	DeflateEnabled  bool `flag:"deflate"`
	MaxDeflateLevel int  `flag:"max-deflate-level"`
	SnappyEnabled   bool `flag:"snappy"`
}

2.nsqd.New 實例化流程

func New(opts *Options) (*NSQD, error) {
	var err error
	//元數據存儲地址,元數據包括 topic 和 channel,具體可參考 meta 結構體
	dataPath := opts.DataPath
	if opts.DataPath == "" {
		cwd, _ := os.Getwd()
		dataPath = cwd
	}
	//日誌
	if opts.Logger == nil {
		opts.Logger = log.New(os.Stderr, opts.LogPrefix, log.Ldate|log.Ltime|log.Lmicroseconds)
	}

	n := &NSQD{
		startTime:            time.Now(),
		topicMap:             make(map[string]*Topic),
		clients:              make(map[int64]Client),
		exitChan:             make(chan int),
		notifyChan:           make(chan interface{}),
		optsNotificationChan: make(chan struct{}, 1),
		dl:                   dirlock.New(dataPath),
	}
	//http 客戶端,專門用於訪問lookupd,上報或獲取client,topic,channel的信息
	httpcli := http_api.NewClient(nil, opts.HTTPClientConnectTimeout, opts.HTTPClientRequestTimeout)
	n.ci = clusterinfo.New(n.logf, httpcli)

	//lookupd 集羣 設置爲空,後面會通過lookup 線程補充
	n.lookupPeers.Store([]*lookupPeer{})
	//存儲配置
	n.swapOpts(opts)
	n.errValue.Store(errStore{})

	//.... 省略中間過程

	//TCP 連接處理(tcp.go)
	n.tcpServer = &tcpServer{}
	//監聽
	n.tcpListener, err = net.Listen("tcp", opts.TCPAddress)
	if err != nil {
		return nil, fmt.Errorf("listen (%s) failed - %s", opts.TCPAddress, err)
	}
	//httP 服務監聽
	n.httpListener, err = net.Listen("tcp", opts.HTTPAddress)
	if err != nil {
		return nil, fmt.Errorf("listen (%s) failed - %s", opts.HTTPAddress, err)
	}
	if n.tlsConfig != nil && opts.HTTPSAddress != "" {
		//https 監聽
		n.httpsListener, err = tls.Listen("tcp", opts.HTTPSAddress, n.tlsConfig)
		if err != nil {
			return nil, fmt.Errorf("listen (%s) failed - %s", opts.HTTPSAddress, err)
		}
	}

	return n, nil
}

3.nsqd.Main 啓動過程

func (n *NSQD) Main() error {
	ctx := &context{n}

	exitCh := make(chan error)
	var once sync.Once
	exitFunc := func(err error) {
		once.Do(func() {
			if err != nil {
				n.logf(LOG_FATAL, "%s", err)
			}
			exitCh <- err
		})
	}

	n.tcpServer.ctx = ctx
	/*
		util.WaitGroupWrapper(internal/util/wait_group_wrapper.go)
		這個方法只是,包裝了 Add,和Done的操作
	*/
	//啓動TCP 服務監聽(tcp.go Handle)
	n.waitGroup.Wrap(func() {
		exitFunc(protocol.TCPServer(n.tcpListener, n.tcpServer, n.logf))
	})

	httpServer := newHTTPServer(ctx, false, n.getOpts().TLSRequired == TLSRequired)
	//啓動Http 服務監聽 (http.go)
	n.waitGroup.Wrap(func() {
		exitFunc(http_api.Serve(n.httpListener, httpServer, "HTTP", n.logf))
	})

	if n.tlsConfig != nil && n.getOpts().HTTPSAddress != "" {
		httpsServer := newHTTPServer(ctx, true, true)
		n.waitGroup.Wrap(func() {
			exitFunc(http_api.Serve(n.httpsListener, httpsServer, "HTTPS", n.logf))
		})
	}

	//啓動延遲隊列和超時隊列等處理線程
	n.waitGroup.Wrap(n.queueScanLoop)
	//啓動集羣上報 client,topic,channel等處理線程
	n.waitGroup.Wrap(n.lookupLoop)
	if n.getOpts().StatsdAddress != "" {
		n.waitGroup.Wrap(n.statsdLoop)
	}

	err := <-exitCh
	return err
}

總結
nsqd的啓動包括:
1.配置NSQD參數(option.go)
2.實例化nsqd(New)
3.啓動tcp服務監聽、http/https監聽、queueScanLoop線程及lookupLoop線程

下次分享:nsqd中tcp 服務監聽部分。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章