github.com/songzhibin97/gkit@v1.2.13/distributed/worker.go (about)

     1  package distributed
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/signal"
     7  	"sync"
     8  	"syscall"
     9  	"time"
    10  
    11  	"github.com/songzhibin97/gkit/distributed/retry"
    12  
    13  	"github.com/pkg/errors"
    14  
    15  	"github.com/songzhibin97/gkit/distributed/task"
    16  )
    17  
    18  var (
    19  	ErrWorkerGracefullyQuit = errors.New("worker quit gracefully")
    20  	ErrWorkerAbruptlyQuit   = errors.New("worker quit abruptly")
    21  )
    22  
    23  // Worker 任务处理
    24  type Worker struct {
    25  	NoUnixSignals     bool `json:"no_unix_signals"` // 是否使用unix信号控制
    26  	bindService       *Server
    27  	Concurrency       int                        `json:"concurrency"`  // 并发数
    28  	ConsumerTag       string                     `json:"consumer_tag"` // 消费者标签
    29  	Queue             string                     `json:"queue"`        // 队列名称
    30  	errorHandler      func(err error)            // 错误处理
    31  	beforeTaskHandler func(task *task.Signature) // 在任务执行前执行
    32  	afterTaskHandler  func(task *task.Signature) // 在任务结束后执行
    33  	preConsumeHandler func(worker *Worker) bool  // 判断是否需要预处理
    34  }
    35  
    36  // SetErrorHandler 设置处理错误函数
    37  func (w *Worker) SetErrorHandler(f func(err error)) {
    38  	w.errorHandler = f
    39  }
    40  
    41  // SetBeforeTaskHandler 设置执行任务前执行函数
    42  func (w *Worker) SetBeforeTaskHandler(f func(task *task.Signature)) {
    43  	w.beforeTaskHandler = f
    44  }
    45  
    46  // SetAfterTaskHandler 设置执行任务后执行函数
    47  func (w *Worker) SetAfterTaskHandler(f func(task *task.Signature)) {
    48  	w.afterTaskHandler = f
    49  }
    50  
    51  // SetPreConsumeHandler 设置预处理函数
    52  func (w *Worker) SetPreConsumeHandler(f func(worker *Worker) bool) {
    53  	w.preConsumeHandler = f
    54  }
    55  
    56  // Quit 触发强制退出
    57  func (w *Worker) Quit() {
    58  	w.bindService.GetController().StopConsuming()
    59  }
    60  
    61  // Process 任务处理
    62  func (w *Worker) Process(signature *task.Signature) error {
    63  	// 如果任务没有注册,快速返回
    64  	if !w.bindService.IsRegisteredTask(signature.Name) {
    65  		return nil
    66  	}
    67  	// 获取注册执行任务的函数
    68  	handler, ok := w.bindService.GetRegisteredTask(signature.Name)
    69  	if !ok {
    70  		return nil
    71  	}
    72  	// 设置任务状态,改为接收状态
    73  	if err := w.bindService.GetBackend().SetStateReceived(signature); err != nil {
    74  		return errors.Wrap(err, "worker set task state to 'received' error, signature id:"+signature.ID)
    75  	}
    76  	exec, err := task.NewTaskWithSignature(handler, signature)
    77  	if err != nil {
    78  		_ = w.handlerFailed(signature, err)
    79  		return err
    80  	}
    81  
    82  	// 设置任务状态,改为开始执行
    83  	if err = w.bindService.GetBackend().SetStateStarted(signature); err != nil {
    84  		return errors.Wrap(err, "worker set task state to 'started' error, signature id:"+signature.ID)
    85  	}
    86  
    87  	// 生命周期前执行
    88  	if w.beforeTaskHandler != nil {
    89  		w.beforeTaskHandler(signature)
    90  	}
    91  	// 生命周期后执行,注册defer
    92  	if w.afterTaskHandler != nil {
    93  		defer w.afterTaskHandler(signature)
    94  	}
    95  	// 任务调用
    96  	results, err := exec.Call()
    97  	if err != nil {
    98  		// 判断err是否是可重试错误
    99  		retryErr, ok := (interface{})(err).(task.ErrRetryTaskLater)
   100  		if ok {
   101  			// 重试
   102  			return w.handlerRetryIn(signature, retryErr.RetryIn())
   103  		}
   104  		// 根据自定义重试次数开始重试
   105  		if signature.RetryCount > 0 {
   106  			return w.handlerRetry(signature)
   107  		}
   108  		// 置为失败
   109  		return w.handlerFailed(signature, err)
   110  	}
   111  	// 任务完成
   112  	return w.handlerSucceeded(signature, results)
   113  }
   114  
   115  // handlerRetry 处理程序重试
   116  func (w *Worker) handlerRetry(signature *task.Signature) error {
   117  	// 设置重试状态
   118  	if err := w.bindService.GetBackend().SetStateRetry(signature); err != nil {
   119  		return errors.Wrap(err, "worker set task state to 'retry' error, signature id:"+signature.ID)
   120  	}
   121  	signature.RetryCount--
   122  
   123  	// 获取间隔时间
   124  	signature.RetryInterval = retry.FibonacciNext(signature.RetryInterval)
   125  
   126  	eta := time.Now().Add(time.Second * time.Duration(signature.RetryInterval))
   127  	signature.ETA = &eta
   128  	w.bindService.helper.Warnf("Task %s failed. Going to retry in %d seconds.", signature.ID, signature.RetryInterval)
   129  	_, err := w.bindService.SendTask(signature)
   130  	return err
   131  }
   132  
   133  // handlerRetry 处理指定错误程序重试
   134  func (w *Worker) handlerRetryIn(signature *task.Signature, retryIn time.Duration) error {
   135  	// 设置重试状态
   136  	if err := w.bindService.GetBackend().SetStateRetry(signature); err != nil {
   137  		return errors.Wrap(err, "worker set task state to 'retry' error, signature id:"+signature.ID)
   138  	}
   139  	eta := time.Now().Add(retryIn)
   140  	signature.ETA = &eta
   141  	w.bindService.helper.Warnf("Task %s failed. Going to retry in %.0f seconds.", signature.ID, retryIn.Seconds())
   142  	_, err := w.bindService.SendTask(signature)
   143  	return err
   144  }
   145  
   146  // handlerSucceeded 处理程序成功状态
   147  func (w *Worker) handlerSucceeded(signature *task.Signature, results []*task.Result) error {
   148  	if err := w.bindService.GetBackend().SetStateSuccess(signature, results); err != nil {
   149  		return errors.Wrap(err, "worker set task state to 'succeeded' error, signature id:"+signature.ID)
   150  	}
   151  
   152  	// 执行任务成功回调
   153  	for _, success := range signature.CallbackOnSuccess {
   154  		for _, result := range results {
   155  			success.Args = append(success.Args,
   156  				task.Arg{
   157  					Type:  result.Type,
   158  					Value: result.Value,
   159  				},
   160  			)
   161  		}
   162  		_, _ = w.bindService.SendTask(success)
   163  	}
   164  
   165  	// 如果没有回调,完成
   166  	if signature.CallbackChord == nil {
   167  		return nil
   168  	}
   169  
   170  	// 不是任务组,完成
   171  	if signature.GroupID == "" {
   172  		return nil
   173  	}
   174  
   175  	// 检查组内任务是否完成
   176  	groupCompleted, err := w.bindService.GetBackend().GroupCompleted(signature.GroupID)
   177  	if err != nil {
   178  		return errors.Wrap(err, "group completed id:"+signature.ID+"group id:"+signature.GroupID)
   179  	}
   180  	// 不是组内最后一个任务
   181  	if !groupCompleted {
   182  		return nil
   183  	}
   184  
   185  	// 调用组回调
   186  	call, err := w.bindService.GetBackend().TriggerCompleted(signature.GroupID)
   187  	if err != nil {
   188  		return fmt.Errorf("TriggerCompleted group %s returned error: %s", signature.GroupID, err)
   189  	}
   190  
   191  	if !call {
   192  		// 已经调用过了
   193  		return nil
   194  	}
   195  
   196  	// 获取组任务状态
   197  	taskStatus, err := w.bindService.GetBackend().GroupTaskStatus(signature.GroupID)
   198  	if err != nil {
   199  		w.bindService.helper.Errorf(
   200  			"Failed to get tasks states for group:[%s]. Task count:[%d]. The chord may not be triggered. Error:[%s]",
   201  			signature.ID,
   202  			signature.GroupTaskCount,
   203  			err,
   204  		)
   205  		return nil
   206  	}
   207  	// 遍历任务状态
   208  	for _, status := range taskStatus {
   209  		if !status.IsSuccess() {
   210  			// 如果有未成功的任务,组任务失败
   211  			return nil
   212  		}
   213  		for _, result := range status.Results {
   214  			signature.CallbackChord.Args = append(signature.CallbackChord.Args,
   215  				task.Arg{Type: result.Type, Value: result.Value})
   216  		}
   217  	}
   218  	_, err = w.bindService.SendTask(signature.CallbackChord)
   219  	return err
   220  }
   221  
   222  // handlerFailed 处理任务失败状态
   223  func (w *Worker) handlerFailed(signature *task.Signature, err error) error {
   224  	if err := w.bindService.GetBackend().SetStateFailure(signature, err.Error()); err != nil {
   225  		return errors.Wrap(err, "worker set task state to 'succeeded' error, signature id:"+signature.ID)
   226  	}
   227  	if w.errorHandler != nil {
   228  		w.errorHandler(err)
   229  	} else {
   230  		w.bindService.helper.Errorf("Failed processing task %s. Error = %v", signature.ID, err)
   231  	}
   232  	for _, _error := range signature.CallbackOnError {
   233  		_error.Args = append([]task.Arg{{Type: "string", Value: err.Error()}}, _error.Args...)
   234  		_, _ = w.bindService.SendTask(_error)
   235  	}
   236  	if signature.StopTaskDeletionOnError {
   237  		return errors.New("StopTaskDeletionOnError")
   238  	}
   239  	return nil
   240  }
   241  
   242  func (w *Worker) ConsumeQueue() string {
   243  	return w.Queue
   244  }
   245  
   246  func (w *Worker) PreConsumeHandler() bool {
   247  	if w.preConsumeHandler != nil {
   248  		return true
   249  	}
   250  	return w.preConsumeHandler(w)
   251  }
   252  
   253  // Start 启动
   254  func (w *Worker) Start() error {
   255  	errChan := make(chan error, 1)
   256  	w.StartSync(errChan)
   257  	return <-errChan
   258  }
   259  
   260  // StartSync 异步启动
   261  func (w *Worker) StartSync(errChan chan<- error) {
   262  	w.bindService.helper.Info("worker start")
   263  	w.bindService.helper.Info("worker tag", w.ConsumerTag)
   264  	w.bindService.helper.Info("use queue", w.Queue)
   265  	controller := w.bindService.GetController()
   266  
   267  	var wg sync.WaitGroup
   268  	go func() {
   269  		for {
   270  			retry, err := controller.StartConsuming(w.Concurrency, w)
   271  			if retry {
   272  				if w.errorHandler != nil {
   273  					w.errorHandler(err)
   274  				} else {
   275  					w.bindService.helper.Warnf("controller consume err: %s", err)
   276  				}
   277  			} else {
   278  				wg.Wait()
   279  				errChan <- err
   280  				return
   281  			}
   282  		}
   283  	}()
   284  	if !w.NoUnixSignals {
   285  		sign := make(chan os.Signal, 1)
   286  		signal.Notify(sign, os.Interrupt, syscall.SIGTERM)
   287  
   288  		var acceptCount uint
   289  		go func() {
   290  			for s := range sign {
   291  				w.bindService.helper.Warnf("signal received: %v", s)
   292  				acceptCount++
   293  				if acceptCount < 2 {
   294  					// 正常退出
   295  					w.bindService.helper.Warn("Waiting for running tasks to finish before shutting down")
   296  					wg.Add(1)
   297  					go func() {
   298  						w.Quit()
   299  						errChan <- ErrWorkerGracefullyQuit
   300  						wg.Done()
   301  					}()
   302  				} else {
   303  					// 重复收到退出信号
   304  					errChan <- ErrWorkerAbruptlyQuit
   305  				}
   306  			}
   307  		}()
   308  	}
   309  }