拉取任务流控
代码入口:temporal-go/internal/internal_worker_base.go
func (bw *baseWorker) Start() {
if bw.isWorkerStarted {
return
}
bw.metricsHandler.Counter(metrics.WorkerStartCounter).Inc(1)
for i := 0; i < bw.options.pollerCount; i++ {
bw.stopWG.Add(1)
go bw.runPoller()
}
bw.stopWG.Add(1)
go bw.runTaskDispatcher()
bw.isWorkerStarted = true
traceLog(func() {
bw.logger.Info("Started Worker",
"PollerCount", bw.options.pollerCount,
"MaxConcurrentTask", bw.options.maxConcurrentTask,
"MaxTaskPerSecond", bw.options.maxTaskPerSecond,
)
})
}
我们可以看到上面的for循环部分:
for i := 0; i < bw.options.pollerCount; i++ {
bw.stopWG.Add(1)
go bw.runPoller()
}
根据配置的参数,启动相应数量的任务拉取器(默认是2个)
那么对于每个任务拉取器来说,拉取任务的流控又是怎样的呢?我们进入具体的代码看看:
func (bw *baseWorker) runPoller() {
defer bw.stopWG.Done()
bw.metricsHandler.Counter(metrics.PollerStartCounter).Inc(1)
for {
select {
case <-bw.stopCh:
return
case <-bw.pollerRequestCh:
if bw.sessionTokenBucket != nil {
bw.sessionTokenBucket.waitForAvailableToken()
}
bw.pollTask()
}
}
}
可以看到,是通过pollerRequestCh
这样的channel控制的,这个channel有几个地方来控制:
在拉取任务的时候,如果拉取失败,则会向channel发送一个消息,通知再次拉取
在任务处理的时候,会根据配置一次性向channel发送相应数量的消息,通知拉取器拉取相应数量的任务(默认是1000个)
在每个任务处理完成的时候,向channel发送一个消息,通知拉取器再次拉取
处理任务流控
下面来看看任务处理的流控,代码入口如下:
func (bw *baseWorker) runTaskDispatcher() {
defer bw.stopWG.Done()
for i := 0; i < bw.options.maxConcurrentTask; i++ {
bw.pollerRequestCh <- struct{}{}
}
for {
// wait for new task or worker stop
select {
case <-bw.stopCh:
return
case task := <-bw.taskQueueCh:
// for non-polled-task (local activity result as task), we don't need to rate limit
_, isPolledTask := task.(*polledTask)
if isPolledTask && bw.taskLimiter.Wait(bw.limiterContext) != nil {
if bw.isStop() {
return
}
}
bw.stopWG.Add(1)
go bw.processTask(task)
}
}
}
上面的代码大致如下:
根据配置,一次性向任务拉取器的channel发送相应数量的消息,通知任务拉取器一次性拉取相应数量的任务(默认是1000个)
从任务队列channel中获取任务
执行任务
下面来看看执行任务:
func (wtp *workflowTaskPoller) processWorkflowTask(task *workflowTask) error {
if task.task == nil {
// We didn't have task, poll might have timeout.
traceLog(func() {
wtp.logger.Debug("Workflow task unavailable")
})
return nil
}
doneCh := make(chan struct{})
laResultCh := make(chan *localActivityResult)
laRetryCh := make(chan *localActivityTask)
// close doneCh so local activity worker won't get blocked forever when trying to send back result to laResultCh.
defer close(doneCh)
for {
var response *workflowservice.RespondWorkflowTaskCompletedResponse
startTime := time.Now()
task.doneCh = doneCh
task.laResultCh = laResultCh
task.laRetryCh = laRetryCh
completedRequest, resetter, err := wtp.taskHandler.ProcessWorkflowTask(
task,
func(response interface{}, startTime time.Time) (*workflowTask, error) {
wtp.logger.Debug("Force RespondWorkflowTaskCompleted.", "TaskStartedEventID", task.task.GetStartedEventId())
heartbeatResponse, err := wtp.RespondTaskCompletedWithMetrics(response, nil, task.task, startTime)
if err != nil {
return nil, err
}
if heartbeatResponse == nil || heartbeatResponse.WorkflowTask == nil {
return nil, nil
}
task := wtp.toWorkflowTask(heartbeatResponse.WorkflowTask)
task.doneCh = doneCh
task.laResultCh = laResultCh
task.laRetryCh = laRetryCh
return task, nil
},
)
if completedRequest == nil && err == nil {
return nil
}
if _, ok := err.(workflowTaskHeartbeatError); ok {
return err
}
response, err = wtp.RespondTaskCompletedWithMetrics(completedRequest, err, task.task, startTime)
if err != nil {
return err
}
if eventLevel := response.GetResetHistoryEventId(); eventLevel != 0 {
resetter(eventLevel)
}
if response == nil || response.WorkflowTask == nil {
return nil
}
// we are getting new workflow task, so reset the workflowTask and continue process the new one
task = wtp.toWorkflowTask(response.WorkflowTask)
}
}
从上面可以看到,启动了一个loop循环,在执行任务完成之后,会再去通过rpc请求从server中获取下一个任务,知道没有任务为止。