【Temporal】Worker任务流控

拉取任务流控

代码入口: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中获取下一个任务,知道没有任务为止。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值