NSQ 源碼分析之NSQD--queueScanLoop

今天主要講的是 NSQD 中 queueScanLoop代碼實現,這個函數的作用是用來維護 channel 中的延時隊列和等待消費確認隊列。將超時的消息重新加入消費隊列中。

主要代碼文件:

1.nsqd/nsqd.go

queueScanLoop  函數在 Main 函數中啓動  n.waitGroup.Wrap(n.queueScanLoop)

func (n *NSQD) queueScanLoop() {
    //傳遞需要處理的 channel
	workCh := make(chan *Channel, n.getOpts().QueueScanSelectionCount)
	responseCh := make(chan bool, n.getOpts().QueueScanSelectionCount) 
	closeCh := make(chan int)
    
    //定時執行 loop 的間隔時間
	workTicker := time.NewTicker(n.getOpts().QueueScanInterval)
	//goroutine 池維護時間
    refreshTicker := time.NewTicker(n.getOpts().QueueScanRefreshInterval)

	channels := n.channels() //獲取當前nsqd 中所有的channels
    //goroutine 池任務處理和維護
	n.resizePool(len(channels), workCh, responseCh, closeCh)

	for {
		select {
		case <-workTicker.C:
			if len(channels) == 0 {
				continue
			}
		case <-refreshTicker.C:
			channels = n.channels()
			n.resizePool(len(channels), workCh, responseCh, closeCh)
			continue
		case <-n.exitChan:
			goto exit
		}

		num := n.getOpts().QueueScanSelectionCount
		if num > len(channels) {
			num = len(channels)
		}

	loop:
        //UniqRands 是作用是,從 channels 中隨機選擇 num 個 channel
		for _, i := range util.UniqRands(num, len(channels)) {
			workCh <- channels[i]
		}
        //等待處理響應,記錄失敗次數
		numDirty := 0
		for i := 0; i < num; i++ {
			if <-responseCh {
				numDirty++
			}
		}
        //失敗次數超過一定比率,重新執行 loop.
		if float64(numDirty)/float64(num) > n.getOpts().QueueScanDirtyPercent {
			goto loop
		}
	}

exit:
	n.logf(LOG_INFO, "QUEUESCAN: closing")
	close(closeCh)
	workTicker.Stop()
	refreshTicker.Stop()
}

resizePool 函數主要作用是維護 goroutine 池的數量。

func (n *NSQD) resizePool(num int, workCh chan *Channel, responseCh chan bool, closeCh chan int) {
    //理想線程池數量
	idealPoolSize := int(float64(num) * 0.25)
	if idealPoolSize < 1 {
		idealPoolSize = 1
	} else if idealPoolSize > n.getOpts().QueueScanWorkerPoolMax {
		idealPoolSize = n.getOpts().QueueScanWorkerPoolMax
	}
	for {
		if idealPoolSize == n.poolSize {
			break
		} else if idealPoolSize < n.poolSize {
			closeCh <- 1 //某個關閉工作線程
			n.poolSize--
		} else {
			//增加工作線程 處理延遲優先級隊列和消費確認優先級隊列
			n.waitGroup.Wrap(func() {
				n.queueScanWorker(workCh, responseCh, closeCh)
			})
			n.poolSize++
		}
	}
}

queueScanWorker 主要處理 channel 中的 延時優先級隊列和消費確認優先級隊列,具體處理流程參考 https://blog.csdn.net/H_L_S/article/details/105155235

func (n *NSQD) queueScanWorker(workCh chan *Channel, responseCh chan bool, closeCh chan int) {
	for {
		select {
		case c := <-workCh:
			now := time.Now().UnixNano()
			dirty := false
			if c.processInFlightQueue(now) { //消費確認優先級隊列
				dirty = true
			}
			if c.processDeferredQueue(now) { //延時優先級隊列處理
				dirty = true
			}
			responseCh <- dirty
		case <-closeCh: //關閉工作線程
			return
		}
	}
}

總結:

queueScanLoop 函數維護並管理 goroutine 池的數量,這些 goroutine 主要用於處理 channel 中 延時優先級隊列和等待消費確認優先級隊列。同時 queueScanLoop 循環隨機選擇 channel 並交給工作線程池進行處理。

下次分享:NSQD 消息協議 Message

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