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

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