Go 例程中基于上下文的长时间运行循环的终止

use*_*134 5 go goroutine

我正在实现一个功能,需要定期从目录中读取文件、解析它们并将其导出到 REST 服务。作为同一部分,我想优雅地处理程序终止(SIGKILL、SIGQUIT 等)。同样,我想知道如何实现基于上下文的进程取消。

为了定期执行流程,我使用 gocron。

cmd/scheduler.go

func scheduleTask(){
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    s := gocron.NewScheduler()
    s.Every(10).Minutes().Do(processTask, ctx)
    s.RunAll()  // run immediate
    <-s.Start() // schedule
    for {
        select {
        case <-(ctx).Done():
            fmt.Print("context done")
            s.Remove(processTask)
            s.Clear()
            cancel()
        default:
        }
    }
}   
func processTask(ctx *context.Context){
  task.Export(ctx)
}
Run Code Online (Sandbox Code Playgroud)

任务/export.go

func Export(ctx *context.Context){
   pendingFiles, err := filepath.Glob("/tmp/pending/" + "*_task.json")
   //error handling
   //as there can be 100s of files, I would like to break the loop when context.Done() to return asap & clean up the resources here as well
   for _, fileName := range pendingFiles {

    exportItem(fileName string)
   }
}

func exportItem(fileName string){
    data, err := ReadFile(fileName)  //not shown here for brevity
    //err handling
    err = postHTTPData(string(data)) //not shown for brevity
    //err handling
}
Run Code Online (Sandbox Code Playgroud)

dm0*_*514 2

对于流程管理,我认为另一个组件是信号的实际处理,以及管理这些信号的上下文。

我不确定 go-cron 的具体细节(他们在 github 上有一个展示其中一些概念的示例),但总的来说,我认为涉及的步骤是:

  • 操作系统信号处理程序的注册
  • 等待接收信号
  • 取消顶级上下文以响应信号

例子:

sigCh := make(chan os.Signal, 1)
defer close(sigCh)
signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT)
<-sigCh
cancel()
Run Code Online (Sandbox Code Playgroud)

我不确定这在 go-cron 的上下文中会是什么样子,但是信号处理代码取消的上下文应该是给定任务和作业的上下文的父级。