前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >errgroup 的基本使用

errgroup 的基本使用

作者头像
LinkinStar
发布2022-10-28 10:39:47
2160
发布2022-10-28 10:39:47
举报

我们在使用 go 编写代码的时候,在错误处理的时候,经常会写出很多 if err != nil ,其实有些时候我们可以使用一些技巧去避免,本文就来讨论两种常见的避免技巧,内部包装错误和 errgroup。

基本 case 实现

package main  
  
import "fmt"  
  
func StartUserService() error {  
   fmt.Println("start user service")  
   return nil  
}  
  
func StartGoodsService() error {  
   fmt.Println("start goods service")  
   return nil  
}  
  
func StartOrderService() error {  
   fmt.Println("start order service")  
   return nil  
}  
  
func main() {  
   err := StartUserService()  
   if err != nil {  
      panic(err)  
   }  
   err = StartGoodsService()  
   if err != nil {  
      panic(err)  
   }  
   err = StartOrderService()  
   if err != nil {  
      panic(err)  
   }  
}

这是一个我们常常见到的情况,就是对于多个不同的方法进行调用,比如启动不同的服务,然后每次启动都会返回一个错误,我们都需要对错误进行处理,那么我们如何去优化这个代码呢?

为了简化问题,这个 case 里面我们讨论的基础是,这些启动服务之间没有关联关系,并且只要有其中一个启动失败就直接退出。

内部操作包装实现

package main  
  
import "fmt"  
  
type ServiceManager struct {  
   err error  
}  
  
type StartFn func() (err error)  
  
func (s *ServiceManager) Start(sf StartFn) {  
   if s.err != nil {  
      return  
   }  
   s.err = sf()  
}  
  
func (s *ServiceManager) Err() error {  
   return s.err  
}  
  
func StartUserService() error {  
   fmt.Println("start user service")  
   return nil  
}  
  
func StartGoodsService() error {  
   fmt.Println("start goods service")  
   return nil  
}  
  
func StartOrderService() error {  
   fmt.Println("start order service")  
   return nil  
}  
  
func main() {  
   sm := &ServiceManager{}  
  
   sm.Start(StartUserService)  
   sm.Start(StartGoodsService)  
   sm.Start(StartOrderService)  
  
   if err := sm.Err(); err != nil {  
      panic(err)  
   }  
}

当我们遇到重复代码想要合并的时候,第一个想法应该就是抽象,将不同的样子的方法进行抽象,抽象成一个接口。这样抽象之后我们往往就可以通过一次代码来实现相同的功能。

上述的代码中,将启动抽象,并且将错误包装到了一个结构的内部,这也是我们常用的一个技巧,这样的好处在于,在主函数中就没有额外的处理逻辑,只需要无脑的进行调用就可以了。

errgroup 实现

package main  
  
import (  
   "context"  
   "fmt"  
   "golang.org/x/sync/errgroup"
)
  
func StartUserService() error {  
   fmt.Println("start user service")  
   return nil  
}  
  
func StartGoodsService() error {  
   fmt.Println("start goods service")  
   return nil  
}  
  
func StartOrderService() error {  
   fmt.Println("start order service")  
   return nil  
}  
  
func main() {  
   gp, _ := errgroup.WithContext(context.Background())  
   gp.Go(StartUserService)  
   gp.Go(StartGoodsService)  
   gp.Go(StartOrderService)  
   if err := gp.Wait(); err != nil {  
      panic(err)  
   }  
}

另一种更为通用的方式是用 errgroup,其实它的原理也是类似的,只不过使用 goroutine 去运行了各个子任务,然后等待子任务全部完成,内部就是通过 waitgroup 实现的。并且当有任意一个出现错误时就会记录错误,最终在 wait 返回。

errgroup 源码见:https://cs.opensource.google/go/x/sync/+/master:errgroup/errgroup.go

扩展

errgroup 还提供了 SetLimitTryGo 方法,通过设定一个并发的上限来确保并发的任务数不会超过限制条件。

总结

  • 本文主要记录了 errgroup 的基本使用,使用明显能比自己亲自使用 waitgroup 要来的方便。
  • 避免重复代码的技巧往往就是,抽象后合并实现,同时使用合理的设计模式
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-10-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基本 case 实现
  • 内部操作包装实现
  • errgroup 实现
    • 扩展
    • 总结
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档