为什么 `http.Client` 的耗时如此之大?

huangapple go评论83阅读模式
英文:

Go http.Client. Why the elapsed time is so big?

问题

我有一个类似这样的代码。简单来说,我正在创建一个带有超时的HTTP客户端,并向某些服务器发送一些请求。

package rtb

import (
    "bytes"
    "net/http"
    "time"
)

var tcpHttpClient *http.Client

func init() {
    tcpHttpClient = &http.Client{
        Timeout:   time.Millisecond * 200,
    }
}

func doRequest(url string, reqBody []byte) {
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqBody))
    if err != nil {
        return
    }
    defer resp.Body.Close()

    // 这一行返回一个错误
    resp, clientError := tcpHttpClient.Do(req)
    
    // ...
}

问题是,有时候经过的时间比超时时间要长得多。我无法理解为什么会这样。

例如:请求未成功,出现错误消息"上下文截止超时(等待标头时超过了Client.Timeout)"。客户端的超时时间为200毫秒,但经过的时间为3秒。据我所知,Client.Timeout应该覆盖从拨号到获取响应正文的整个过程。

那么,为什么经过的时间比超时时间长?为什么请求没有更早地被取消?

英文:

I have a code like this. To be short, I'm creating the http client with a timeout and doing some request to some server.

package rtb

import (
    "bytes"
    "net/http"
    "time"
)

var tcpHttpClient *http.Client

func init() {
    tcpHttpClient = &http.Client{
        Timeout:   time.Millisecond * 200,
    }
}

func doRequest(url string, reqBody []byte) {
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqBody))
    if err != nil {
        return
    }
    defer resp.Body.Close()

    // This line returns an error
    resp, clientError := tcpHttpClient.Do(req)
    
    // ...
}

The problem is, sometimes the elapsed time is a WAY lot bigger than the timeout itself. And I can't understand why.

Example: The request has not succeeded with the error message "context deadline exceeded (Client.Timeout exceeded while awaiting headers)".
The timeout of the client is 200ms, but the elapsed time is 3 seconds. As far as I know, the Client.Timeout should cover all the way from the dial to the getting the response body.

So, why the elapsed time is bigger than the timeout? Why the request has not been cancelled earlier?

答案1

得分: 2

简而言之,当计算机发出HTTP请求时,会发生很多事情。没有一个并行运行的计时器来不断检查超时并尽快取消HTTP请求。但在所有必要的步骤之间,会检查是否超过了超时时间。当超时时间超过时,系统开始取消请求并执行其他清理任务。在您的情况下,第一个检查到超时时间已过的检查发生在三秒钟之后。

因此,直到返回错误的时间可能比指定的超时时间要长得多,但给定的限制是受到尊重的。

顺便提一下:当进行许多不受您控制的异步任务(例如通过网络进行HTTP请求)时,可以使用Go协程来解除主协程的阻塞。

英文:

In a nutshell and very simplified: There is a lot happening when a computer makes an HTTP request. There is no timer running in parallel that constantly checks the timeout and cancels the HTTP request as soon as possible. But between all necessary steps there is a check if the timeout is exceeded. When the timeout is exceeded, the system starts to cancel the request and does other cleanup tasks. In your case the first check that noticed that the timeout was exceeded happened after three seconds.

So it might take much longer than the specified timeout until the error is returned, but the given limit is respected.

As a side note: When making a lot of asynchronous tasks that are out of your control (like doing an HTTP request over a network) go routines can be used to unblock your main routine.

huangapple
  • 本文由 发表于 2021年6月10日 16:27:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/67917468.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定