如何在Golang中设置http.Get()请求的超时?

如何在Golang中设置http.Get()请求的超时?,http,get,timeout,go,Http,Get,Timeout,Go,我正在Go中制作一个URL获取程序,并有一个URL列表要获取。我向每个URL发送http.Get()请求并获取它们的响应 resp,fetch_err := http.Get(url) 如何为每个Get请求设置自定义超时?(默认时间很长,这使得我的抓取程序非常慢。)我希望我的抓取程序有一个大约40-45秒的超时时间,之后它应该返回“请求超时”,并转到下一个URL 我如何才能做到这一点?一种快速而肮脏的方法: http.DefaultTransport.(*http.Transport).Res

我正在Go中制作一个URL获取程序,并有一个URL列表要获取。我向每个URL发送
http.Get()
请求并获取它们的响应

resp,fetch_err := http.Get(url)
如何为每个Get请求设置自定义超时?(默认时间很长,这使得我的抓取程序非常慢。)我希望我的抓取程序有一个大约40-45秒的超时时间,之后它应该返回“请求超时”,并转到下一个URL

我如何才能做到这一点?

一种快速而肮脏的方法:

http.DefaultTransport.(*http.Transport).ResponseHeaderTimeout = time.Second * 45
这是在不进行任何协调的情况下改变全局状态。然而,对于你的url抓取程序来说,这可能还可以。否则,创建http.RoundTripper的私有实例:

var myTransport http.RoundTripper = &http.Transport{
        Proxy:                 http.ProxyFromEnvironment,
        ResponseHeaderTimeout: time.Second * 45,
}

var myClient = &http.Client{Transport: myTransport}

resp, err := myClient.Get(url)
...

上面没有测试任何内容。

您需要使用 定制环绕的拨号功能

类似(完全未经测试):


要添加到Volker的答案中,如果您还想在连接超时之外设置读/写超时,您可以执行以下操作

package httpclient

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

func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) {
    return func(netw, addr string) (net.Conn, error) {
        conn, err := net.DialTimeout(netw, addr, cTimeout)
        if err != nil {
            return nil, err
        }
        conn.SetDeadline(time.Now().Add(rwTimeout))
        return conn, nil
    }
}

func NewTimeoutClient(connectTimeout time.Duration, readWriteTimeout time.Duration) *http.Client {

    return &http.Client{
        Transport: &http.Transport{
            Dial: TimeoutDialer(connectTimeout, readWriteTimeout),
        },
    }
}
此代码经过测试并正在生产中运行。此处提供了测试的完整要点

请注意,您将需要为每个请求创建一个新的客户端,因为
conn.setDudate
引用了
时间的未来点。现在()

您可以使用它以一种方式和简单的方式处理超时。

显然,在Go 1.3中有超时字段

client := http.Client{
    Timeout: 5 * time.Second,
}
client.Get(url)

对我来说,这就完成了。

如果您想按请求执行,为简洁起见,错误处理将被忽略:

ctx, cncl := context.WithTimeout(context.Background(), time.Second*3)
defer cncl()

req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://google.com", nil)

resp, _ := http.DefaultClient.Do(req)


这可能会有所帮助,但请注意,
ResponseHeaderTimeout
只有在建立连接后才会启动。

非常感谢!这正是我要找的东西。使用net.DialTimeout比zzzz回答中描述的Transport.ResponseHeaderTimeout有什么好处?@Daniel B:你问错问题了。这不是关于优势,而是关于每种代码的功能。如果无法呼叫服务器,则会跳转拨号超时;如果建立连接上的某些操作花费的时间过长,则会跳转其他超时。如果您的目标服务器很快建立了连接,但随后又开始变慢,那么拨号超时将无济于事。@Volker,谢谢您的回答。实际上我也意识到了这一点:看起来Transport.ResponseHeaderTimeout设置了读取超时,即建立连接后的超时,而您的是拨号超时。dmichael的解决方案同时处理拨号超时和读取超时。@Jonno:Go中没有强制转换。这些是类型转换。请任何人纠正我,但看起来ResponseHeaderTimeout是关于读取超时的,即建立连接后的超时。最全面的解决方案似乎是@dmichael的解决方案,因为它允许设置拨号超时和读取超时。
http.DefaultTransport。(*http.Transport)。ResponseHeaderTimeout=time.Second*45
帮助我进行请求超时的写测试。非常感谢。您不应该检查conn.setDailate的返回值吗?此超时不适用于keepalive连接,这是默认设置,我想大多数人都应该使用它。下面是我处理这一问题的方法:感谢@xitrium和Eric提供的额外输入。我觉得这并不像您所说的那样,我们需要为每个请求创建一个新的客户端。因为拨号是一个功能,我想它每次都会被调用,你在同一个客户端发送每个请求。你确定每次都需要一个新客户端吗?每次拨号时,它都会使用TimeoutDialer构建的功能,而不是使用net.Dial。这是一个新的连接,每次都评估最后期限,从一个新的time.Now()调用。好吧,这对我来说已经足够好了。很高兴我向下滚动了一点:)有没有一种方法可以让每个请求都有不同的超时?当超时到达时会发生什么?
Get
是否返回错误?我有点困惑,因为
客户端的Godoc告诉我:Get、Head、Post或Do返回后计时器仍在运行,并且会中断Response.Body的读取。那么,这是否意味着
Get
或读取
Response.Body
可能会被错误打断?问题,
http.Client.Timeout
http.Transport.ResponseHeaderTimeout
之间有什么区别?@Roylee根据文档的主要区别之一:
http.Client.Timeout
包括读取响应正文的时间,
http.Transport.ResponseHeaderTimeout
不包括它。只是让你们知道我发现这种方式更方便(如果有网络问题,拨号超时不起作用,至少对我来说是这样):@Audrius知道当有网络问题时,拨号超时为什么不起作用吗?我想我也看到了同样的事情。我以为这就是拨号超时的原因@Jordan很难说,因为我没有深入研究库代码。我已经在下面发布了我的解决方案。我在生产中使用它已经有一段时间了,到目前为止,它“只工作”(tm)。这个库看起来棒极了!额外信息:根据文档,上下文规定的截止日期还包括阅读正文,类似于
http.Client.Timeout
。应该是Go 1.7+的公认答案。对于Go 1.13+,可使用
ctx, cncl := context.WithTimeout(context.Background(), time.Second*3)
defer cncl()

req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://google.com", nil)

resp, _ := http.DefaultClient.Do(req)
timeout := time.Duration(5 * time.Second)
transport := &http.Transport{Proxy: http.ProxyURL(proxyUrl), ResponseHeaderTimeout:timeout}