【问题标题】:Reusing Keep-Alive Connection in case of Timeout in Golang在 Golang 超时的情况下重用 Keep-Alive 连接
【发布时间】:2018-03-20 14:06:30
【问题描述】:
/* Keep Alive Client*/
HttpClient{
        Client: &http.Client{
            Transport: &http.Transport{
                Dial: (&net.Dialer{
                    Timeout:   dialTimeout,
                    KeepAlive: dialTimeout * 60,
                }).Dial,
                DisableKeepAlives:   false,
                MaxIdleConnsPerHost: idleConnectionsPerHost,
            },
        },
        Timeout: 5 * time.Second,
    }

/* Execute Request */
        timeoutContext, cancelFunction := context.WithTimeout(context.Background(), self.Timeout)

            defer cancelFunction()
            if response, err = self.Client.Do(request.WithContext(timeoutContext)); err == nil {
                defer response.Body.Close()

                /* Check If Request was Successful */
                statusCode = response.StatusCode
                if response.StatusCode == http.StatusOK {
                    /* Read Body & Decode if Response came & unmarshal entity is supplied */
                    if responseBytes, err = ioutil.ReadAll(response.Body); err == nil && unmarshalledResponse != nil {
                        //Process Response
                    }
                } else {
                    err = errors.New(fmt.Sprintf("Non 200 Response. Status Code: %v", response.StatusCode))
                }
            }

在 Golang 中,只要 Keep-Alive 连接中的请求超时,该连接就会丢失并被重置。对于上面的代码,如果超时,在 Wireshark 中查看数据包表明 RST 是由客户端发送的,因此不再重用连接。

事件尝试使用 Httpclient 的 Timeout 而不是 ContextWithTimeout,但在连接被重置的地方有类似的发现。

无论如何,即使在请求超时的情况下,我们也可以保持已建立的 keep-alive 连接。

【问题讨论】:

  • 如果请求超时,则意味着它在某种意义上还没有“完成”,所以它处于未定义状态。你不能重复使用它。
  • 如果您希望重用连接,您还需要确保您正在阅读整个响应正文。在您的代码中,任何非 200 响应代码都将导致响应正文不被读取,因此连接不会被重用。
  • @Adrian 在我目前的理解中 defer response.Body.Close() 也应该读取响应正文并丢弃它。让我知道它是否有任何参考不正确。
  • Close 关闭流,它不会读取任何未读的正文内容。
  • 我也试过了,但在 Wireshark 中观察到时仍然发送 RST。

标签: go tcp keep-alive


【解决方案1】:

net/http 客户端超时关闭连接,因为连接不能被重用。

考虑如果重用连接会发生什么。如果客户端在超时之前没有收到来自服务器的响应或部分响应,那么下一个请求将读取前一个响应的一部分。

要在超时时保持连接活动,请在应用程序代码中实现超时。继续在 net/http 客户端中使用更长的超时时间来处理连接真正卡住或死掉的情况。

result := make(chan []byte)
go func() {
   defer close(result)
   resp, err := client.Do(request)
   if err != nil {
        // return
   }
   defer resp.Body.Close()
   if resp.StatusCode == http.StatusOK {
      p, err := ioutil.ReadAll(resp.Body)
      if err != nil {
         return
      }
      result <- resp
  }
}()

select {
case p, ok <- result:
   if ok {
      // p is the response body
   }
case time.After(timeout):
   // do nothing
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-13
    • 2012-03-12
    • 1970-01-01
    • 1970-01-01
    • 2017-02-19
    相关资源
    最近更新 更多