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 服務監聽部分。