【问题标题】:Goroutine Timeout协程超时
【发布时间】:2018-07-07 23:35:47
【问题描述】:
type Response struct {
  data   interface{}
  status bool
}

func Find() (interface{}, bool) {
  ch := make(chan Response, 1)

  go func() {
    data, status := findCicCode()
    ch <- Response{data: data, status: status}
  }()

  select {
  case response := <-ch:
    return response.data, response.status
  case <-time.After(50 * time.Millisecond):
    return "Request timed out", false
  }
}

所以,我有上述功能。基本上findCicCode() 函数调用在内部对外部服务进行了 3 次 http 调用。我在这里为这 3 个 http 调用添加了组合超时。在我的情况下不能单独超时。但是如果超过超时,它仍然会在后台进行api调用。

我不确定这里是否存在 goroutine 泄漏。如果超时,有没有办法取消这些 https 请求?

【问题讨论】:

    标签: go goroutine go-iris


    【解决方案1】:

    您可以使用 context.Context 控制 http 请求的取消。

    // create a timeout or cancelation context to suit your requirements
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()
    
    req, err := http.NewRequest("GET", location, nil)
    
    // add the context to each request and they will be canceled in unison
    resp, err := http.Do(req.WithContext(ctx))
    

    【讨论】:

    • 是的,您只能取消一个 http 请求,但有没有办法让所有 3 个 http 请求都有一个超时。在我的场景中,我有 3 个 http 请求,我希望这 3 个请求的总超时时间为 150 毫秒。我不能把它放在单独的 http 请求中。
    • @PandurangWaghulde:我不明白,你可以在一个上下文中取消任意数量的请求。
    • 只需要对上面的代码(缺少括号),ctx,cancel := context.WithTimeout(context.Background(), time.Second) 做些小改动
    【解决方案2】:

    如果您愿意,您可以通过在通道上(在主 goroutine 中)进行单个接收操作,以及任何其他 goroutine 首先到达其发送操作——time.Sleep 或做实际工作的人——获胜。

    这是一个完整的可运行示例/模拟。调整超时和延迟值以模拟不同的场景。通道是无缓冲的,在读取单个值后关闭,以允许另一个 goroutine 在发送时退出。

    package main
    
    import(
        "fmt"
        "time"
    )
    
    type Response struct {
        Data        []byte
        Status      int
    }
    
    func Wait(s int) {
        time.Sleep(time.Duration(s) * time.Second)
    }
    
    func FindWrapper(ch chan Response, delay int) {
        // Put real find stuff here...
    
        // Dummy response after wait for testing purposes
        Wait(delay)
        ch <- Response{[]byte("Some data..."), 200}
    }
    
    func main() {
        timeout := 3
        delay := 4
        ch := make(chan Response)
    
        // whoever sends to ch first wins...
        go func() {
            Wait(timeout)
            ch <- Response{}
        }()
        go FindWrapper(ch, delay)
    
        r := <-ch
        close(ch)
        if r.Data == nil {
            r.Status = 500 // or whatever you want for timeout status
        }
        fmt.Printf("Data: %s  Status: %d\n", string(r.Data), r.Status)
    }
    

    缓冲通道也可以。您可以使用 sync.WaitGroup 完成相同的操作,您只需调用一次 Add,然后在 wg.Wait() 之后关闭频道。

    也就是说,我建议尝试 JimB 的使用 Context 超时的解决方案,因为它可能适用于您的用例并且是一个不太复杂的解决方案。

    【讨论】:

      猜你喜欢
      • 2017-06-15
      • 2018-05-18
      • 2020-02-23
      • 2020-01-07
      • 2019-06-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多