• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    Go 协程超时控制的实现

    Go 协程超时控制

    先说个场景:

    假设业务中 A 服务需要调用 服务B,要求设置 5s 超时,那么如何优雅实现?

    Select 超时控制

    考虑是否可以用 select + time.After 方式进行实现

    这里主要利用的是通道在携程之间通信的特点,当程序调用成功后,会向通道中发送信号。没调用成功前,通道会阻塞。

    select {
     case res := -c2:
      fmt.Println(res)
     case -time.After(time.Second * 3):
      fmt.Println("timeout 2")
     }
    

    当 c2 通道中有数据时,并且超时时间没有达到 3s,走 case res := -c2 这个业务逻辑,当超时时间达到 3s , 走的 case -time.After(time.Second * 3) 这个业务逻辑, 这样就可以实现超时 3s 的控制。

    res:= -c2 是因为channel 可以实现阻塞,那么 time.After 为啥可以阻塞呢?

    看 After 源码。sleep.go 可以看到其实也是 channel

    func After(d Duration) -chan Time {
     return NewTimer(d).C
    }

    完整代码示例:

    package timeout
    
    import (
     "fmt"
     "testing"
     "time"
    )
    
    func TestSelectTimeOut(t *testing.T) {
     // 在这个例子中, 假设我们执行了一个外部调用, 2秒之后将结果写入c1
     c1 := make(chan string, 1)
     go func() {
      time.Sleep(time.Second * 2)
      c1 - "result 1"
     }()
     // 这里使用select来实现超时, `res := -c1`等待通道结果,
     // `- Time.After`则在等待1秒后返回一个值, 因为select首先
     // 执行那些不再阻塞的case, 所以这里会执行超时程序, 如果
     // `res := -c1`超过1秒没有执行的话
     select {
     case res := -c1:
      fmt.Println(res)
     case -time.After(time.Second * 1):
      fmt.Println("timeout 1")
     }
     // 如果我们将超时时间设为3秒, 这个时候`res := -c2`将在
     // 超时case之前执行, 从而能够输出写入通道c2的值
     c2 := make(chan string, 1)
     go func() {
      time.Sleep(time.Second * 2)
      c2 - "result 2"
     }()
     select {
     case res := -c2:
      fmt.Println(res)
     case -time.After(time.Second * 3):
      fmt.Println("timeout 2")
     }
    }

    运行结果:

    === RUN   TestSelectTimeOut
    timeout 1
    result 2
    --- PASS: TestSelectTimeOut (3.00s)
    PASS

    go timer 计时器

    这个是 timer 类似的计时器实现,通用也是通过通道来发送数据。

    package main
    import "time"
    import "fmt"
    func main() {
      // Ticker使用和Timer相似的机制, 同样是使用一个通道来发送数据。
      // 这里我们使用range函数来遍历通道数据, 这些数据每隔500毫秒被
      // 发送一次, 这样我们就可以接收到
      ticker := time.NewTicker(time.Millisecond * 500)
      go func() {
        for t := range ticker.C {
        fmt.Println("Tick at", t)
        }
      }()
      // Ticker和Timer一样可以被停止。 一旦Ticker停止后, 通道将不再
      // 接收数据, 这里我们将在1500毫秒之后停止
      time.Sleep(time.Millisecond * 1500)
      ticker.Stop()
      fmt.Println("Ticker stopped")
    }

    go context

    context 监听是否有 IO 操作,开始从当前连接中读取网络请求,每当读取到一个请求则会将该cancelCtx传入,用以传递取消信号,可发送取消信号,取消所有进行中的网络请求。

      go func(ctx context.Context, info *Info) {
       timeLimit := 120
       timeoutCtx, cancel := context.WithTimeout(ctx, time.Duration(timeLimit)*time.Millisecond)
       defer func() {
        cancel()
        wg.Done()
       }()
       resp := DoHttp(timeoutCtx, info.req)
      }(ctx, info)

    关键看业务代码: resp := DoHttp(timeoutCtx, info.req) 业务代码中包含 http 调用 NewRequestWithContext

    req, err := http.NewRequestWithContext(ctx, "POST", url, strings.NewReader(paramString))

    上面的代码,设置了过期时间,当DoHttp(timeoutCtx, info.req) 处理时间超过超时时间时,会自动截止,并且打印 context deadline exceeded。

    看个代码:

    package main
    
    import (
     "context"
     "fmt"
     "testing"
     "time"
    )
    
    func TestTimerContext(t *testing.T) {
     now := time.Now()
     later, _ := time.ParseDuration("10s")
    
     ctx, cancel := context.WithDeadline(context.Background(), now.Add(later))
     defer cancel()
     go Monitor(ctx)
    
     time.Sleep(20 * time.Second)
    
    }
    
    func Monitor(ctx context.Context) {
     select {
     case -ctx.Done():
      fmt.Println(ctx.Err())
     case -time.After(20 * time.Second):
      fmt.Println("stop monitor")
     }
    }

    运行结果:

    === RUN   TestTimerContext
    context deadline exceeded
    --- PASS: TestTimerContext (20.00s)
    PASS

    Context 接口有如下:

    type Context interface {
        Deadline() (deadline time.Time, ok bool)
        Done() -chan struct{}
        Err() error
        Value(key interface{}) interface{}
    }
    

    到此这篇关于Go 协程超时控制的实现的文章就介绍到这了,更多相关Go 协程超时控制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    您可能感兴趣的文章:
    • 一文搞懂如何实现Go 超时控制
    • GoLang之使用Context控制请求超时的实现
    • Go语言利用time.After实现超时控制的方法详解
    • 详解Golang 中的并发限制与超时控制
    上一篇:基于HLS创建Golang视频流服务器的优缺点
    下一篇:Go应该如何实现二级缓存
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    Go 协程超时控制的实现 协程,超时,控制,的,实现,