我正在使用Go语言编写一个URL抓取程序,有一系列需要抓取的URL。我对每个URL发送http.Get()
请求并获取响应。
resp,fetch_err := http.Get(url)
我该怎么为每个Get请求设置自定义超时时间? (默认时间太长了,这使得我的抓取器变得非常缓慢。) 我希望我的抓取器在约40-45秒后超时,然后返回“请求超时”,并继续下一个URL。
我该如何实现这个?
我正在使用Go语言编写一个URL抓取程序,有一系列需要抓取的URL。我对每个URL发送http.Get()
请求并获取响应。
resp,fetch_err := http.Get(url)
我该怎么为每个Get请求设置自定义超时时间? (默认时间太长了,这使得我的抓取器变得非常缓慢。) 我希望我的抓取器在约40-45秒后超时,然后返回“请求超时”,并继续下一个URL。
我该如何实现这个?
据说在 Go 1.3 中,http.Client 有一个超时字段(Timeout field)。
client := http.Client{
Timeout: 5 * time.Second,
}
client.Get(url)
那对我起了作用。
Get
方法会返回一个错误吗?我有点困惑,因为Client
的Godoc文档上说:在Get
、Head
、Post
或Do
方法返回后,计时器仍在运行,并且会中断读取Response.Body
。那么这是否意味着Get
方法或读取Response.Body
方法任一一个都可能被错误中断? - Avi Flaxhttp.Client.Timeout
和 http.Transport.ResponseHeaderTimeout
之间的区别是什么? http.Client.Timeout
控制整个请求的超时时间,包括连接建立、数据传输和响应接收。如果在此时间内没有完成请求,则出现超时错误。默认为无限制。http.Transport.ResponseHeaderTimeout
控制等待服务器发送响应头的时间。如果在此时间内未收到响应头,则会出现超时错误。默认为 0,表示不设置超时限制。http.Client.Timeout
包括读取响应体所需的时间,而 http.Transport.ResponseHeaderTimeout
不包括它。 - imwill你需要使用自己的客户端和自己的传输层,其中包含一个自定义的 Dial 函数,该函数将包装 DialTimeout。
类似这样(完全未经测试):这个:
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")
}
time.Duration(2 * time.Second)
--> time.Duration(2) * time.Second
- Jonno如果您想根据请求执行它,则由于简洁起见,错误处理被忽略:
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)
http.Client.Timeout
。 - kubanczyk补充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),
},
}
}
这段代码已经通过测试并在生产环境中运行。完整的包含测试代码的要点在此处可用: https://gist.github.com/dmichael/5710968
请注意,由于conn.SetDeadline
引用了time.Now()
之后的一个时间点,因此每个请求都需要创建一个新的客户端。
Go的http
模块有几个客户端超时,当前的答案中有一些关于这些超时的示例。
下面是一张图片,用于说明客户端超时情况,请参见完整指南:Go net/http超时
设置HTTP请求超时有两种方法:
client := http.Client{
Timeout: 3 * time.Second,
}
resp, err := client.Do(req)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, URL)
- 使用上下文是请求特定的,而使用客户端超时可能适用于传递给
Do
方法的所有请求客户端具有。- 如果您想要为每个请求专门设置
deadline/timeout
,则使用上下文,否则,如果您想为每个出站请求使用1个超时,则使用客户端超时就足够了。
一种快速而简单的方法:
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)
...
以上内容均未经过测试。
http.DefaultTransport.(*http.Transport).ResponseHeaderTimeout = time.Second * 45
这句话对我在编写请求超时测试方面帮助很大。非常感谢。 - leetimeout := time.Duration(5 * time.Second)
transport := &http.Transport{Proxy: http.ProxyURL(proxyUrl), ResponseHeaderTimeout:timeout}
这可能有所帮助,但请注意ResponseHeaderTimeout
仅在建立连接之后开始计时。