如何使用errgroup捕获runner.go中run函数持续运行时产生的错误
问题排查
你当前无法捕获run函数错误的核心原因是:
Start函数中调用run(ctx)时直接丢弃了返回的错误,没有将错误向上传递给errgroup托管的goroutine- errgroup只有当绑定的goroutine返回非nil错误时才会捕获到错误,你当前
Start函数除了ctx退出时返回nil,其余场景永远不会返回值,自然无法把run的错误抛给上层。
实现方案
分两种常见场景实现,你可以按需选择:
场景1:单次run报错就终止整个运行流程
直接修改runner.go中的Start函数,捕获到run的错误后直接向上返回即可:
func Start(ctx context.Context, dur time.Duration) error { ticker := time.NewTicker(dur) defer ticker.Stop() for { select { case <-ctx.Done(): return nil case <-ticker.C: // 接收run的返回值,非nil则向上传递 if err := run(ctx); err != nil { return err } } } }
修改后运行逻辑:
第一次定时触发时run返回错误,Start把错误返回给errgroup的执行函数,errgroup自动取消关联的ctx,g.Wait()会返回该错误,你可以直接在主函数中捕获打印。
场景2:单次run报错不终止,持续运行同时捕获所有错误
如果你需要让定时任务持续运行,只需要记录每次run的错误,不要因为单次报错就停掉整个流程,可以用错误回调或者通道传递错误:
调整main.go代码
package main import ( "context" "fmt" "awesomeProject/runner" "golang.org/x/sync/errgroup" "time" ) func main() { ctx := context.Background() g, ctx := errgroup.WithContext(ctx) // 初始化错误通道接收所有run的错误 errChan := make(chan error, 10) g.Go(func() (err error) { // 传入错误处理回调 return runner.Start(ctx, 1 * time.Second, func(e error) { errChan <- e }) }) // 单独启动goroutine处理捕获到的错误 go func() { for e := range errChan { fmt.Printf("捕获到run函数错误: %v\n", e) } }() if err := g.Wait(); err != nil { fmt.Println("服务运行错误:", err) } close(errChan) fmt.Println("app terminated") }
调整runner.go代码
package runner import ( "context" "errors" "fmt" "time" ) // 新增errHandler参数用于传递错误 func Start(ctx context.Context, dur time.Duration, errHandler func(error)) error { ticker := time.NewTicker(dur) defer ticker.Stop() for { select { case <-ctx.Done(): return nil case <-ticker.C: if err := run(ctx); err != nil { // 把错误抛给上层回调处理 errHandler(err) } } } } func run(ctx context.Context) error { fmt.Println(time.Now().String()) return errors.New("run:error") }
修改后每次run报错都会通过回调传递给错误处理逻辑,定时任务会持续运行不会终止。
内容的提问来源于stack exchange,提问作者Serhat Morkoç
相关产品推荐
相关产品推荐

