golang超时不控制,那你还配是gopher

769 阅读3分钟

一个人说起自己时,便会得意忘形。——梅里美《卡门》

更好的阅读体验请移步到微信公众号: golang超时不控制,那你还配是gopher?

1. 前言

为什么超时需要控制,这个大家知道吗?自己在脑瓜子里面回想下曾经写过的代码超时控制怎么实现的。那么今天就给大家带来一篇超时控制实战篇,助你在项目中得心应手,废话不多说,直接上干货。

2. 为什么超时需要控制

  • 请求时间过长,用户侧可能已经离开本页面了,服务端还在消耗资源处理,得到的结果没有意义。
  • 过长时间的服务端处理会占用过多资源,导致并发能力下降,甚至出现不可用事故。

golang中一般一个请求是由多个串行或并行的子任务来完成的,每个子任务可能是另外的内部请求,那么当这个请求超时的时候,我们就需要快速返回,释放占用的资源,比如goroutine,文件描述符等。

3. 平民版逻辑

func Do(job interface{}) error {
  //模拟超时任务或者请求
 time.Sleep(time.Minute)
 return nil
}

func requestDo(ctx context.Context, job interface{}) error {
  return Do(job)
}

这样的实现可能用户无法忍受,因为一旦调用Do发生超时,用户侧就会等待很长一段时间没有响应,进而用户带着抱怨离开了页面,真是难过啊。

4. 精英版逻辑

func requestDo(ctx context.Context, job interface{}) error {
 ctx, cancel := context.WithTimeout(ctx, time.Second*2)
 defer cancel()

 done := make(chan error)
 go func() {
    //在这里调用超时控制请求或者任务
  done <- Do(job)
 }()

 select {
  case err := <-done: //请求返回
   return err
  case <-ctx.Done(): //超时控制
   return ctx.Err()
 }
}

大家发现问题了吗,亲爱的伙伴们,带着你们的慧眼,识别代码的bug,来来来。

好,那我给大家说下哪里有问题:

如果超时被控制返回了 即命中case <-ctx.Done(),那么直接就结束当前requestDo协称,但是我们的Do协称还在继续执行呢,当它准备返回的时候发现done没有goroutine接受了,那么Do协称就会一直卡在那里,导致协称泄漏啊

5. 超人版逻辑

解决上面bug的方案之一就是给done初始化一个size大小的空间。

done := make(chan error1)

这样的话,Do协称超时返回了,自动把结果写入done中,然后Do协称会自动退出,这样协称就不会泄漏了。

那有人会问,写进去之后会不会有影响啊,毕竟done没有人去接受了,那么这里明确告诉大家没有影响的,chan毕竟是一个对象,golang的channel资源是可以自动GC掉的

6. 如果Do发生panic了呢

如果Do发生panic,此时就会发现panic是无法被捕获的,原因是因为在 requestDo内部起的goroutine里产生的panic其它goroutine无法捕获。

解决办法就是: requestDo里加上panicChan来处理,同样,需要 panicChan的buffer size为1,如下

panicChan := make(chan interface{}, 1)

详细请看:

func requestDo(ctx context.Context, job interface{}) error {
 ctx, cancel := context.WithTimeout(ctx, time.Second*2)
 defer cancel()

 done := make(chan error)
 panicChan := make(chan interface{}, 1)
 go func() {
  defer func() {
   if p := recover(); p != nil {
    panicChan <- p
   }
  }()
  done <- Do(job)
 }()

 select {
  case err := <-done:
   return err
  case p := <-panicChan: 
   panic(p) //这里也需要panic
  case <-ctx.Done():
   return ctx.Err()
 }
}

改完就可以在requestDo的调用方处理panic 了。

7. 小结

大家看到了吧,通过以上知识点的学习,我相信大家可以写出更加健全的超时控制逻辑了,那还在等什么,快快实践吧。

8. 关注公众号

微信公众号:堆栈future

希望大家关注哈,原创不容易,求点赞,求关注,求分享