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

How to set timeout for http.Get() requests in Golang?

我在Go中创建一个URL提取器,并有一个要获取的URL列表。 我向每个URL发送http.Get()个请求并获取其响应。

1
resp,fetch_err := http.Get(url)

如何为每个Get请求设置自定义超时? (默认时间很长,这使得我的提取器非常慢。)我希望我的提取器有超过40-45秒的超时时间,之后它应该返回"请求超时"并继续下一个URL。

我怎样才能做到这一点?


显然在Go 1.3中,http.Client有Timeout字段

1
2
3
4
5
timeout := time.Duration(5 * time.Second)
client := http.Client{
    Timeout: timeout,
}
client.Get(url)

这对我来说已经成功了。


您需要使用自己的传输设置自己的客户端,该传输使用的是
自定义拨号功能,包含DialTimeout。

像(完全未经测试)这样的东西:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var timeout = time.Duration(2 * time.Second)

func dialTimeout(network, addr string) (net.Conn, error) {
    return net.DialTimeout(network, addr, timeout)
}

func main() {
    transport := http.Transport{
        Dial: dialTimeout,
    }

    client := http.Client{
        Transport: &transport,
    }

    resp, err := client.Get("http://some.url")
}


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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),
        },
    }
}

此代码已经过测试并正在生产中。这里有完整的测试要点
https://gist.github.com/dmichael/5710968

请注意,您需要为每个请求创建一个新客户端,因为conn.SetDeadline将来引用一个点time.Now()


如果您希望按请求执行此操作,则会因为简洁而忽略错误处理:

1
2
3
4
5
6
ctx, cncl := context.WithTimeout(context.Background(), time.Second*3)
defer cncl()

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

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

快速而肮脏的方式:

1
http.DefaultTransport.(*http.Transport).ResponseHeaderTimeout = time.Second * 45

这是在没有任何协调的情况下改变全球状态。但是你的网址提取器可能还可以。否则创建一个http.RoundTripper的私有实例:

1
2
3
4
5
6
7
8
9
var myTransport http.RoundTripper = &http.Transport{
        Proxy:                 http.ProxyFromEnvironment,
        ResponseHeaderTimeout: time.Second * 45,
}

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

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

以上没有测试过。


您可以使用https://github.com/franela/goreq以时尚和简单的方式处理超时。


1
2
timeout := time.Duration(5 * time.Second)
transport := &http.Transport{Proxy: http.ProxyURL(proxyUrl), ResponseHeaderTimeout:timeout}

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