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

pym*_*ymd 91 timeout get http go

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

resp,fetch_err := http.Get(url)
Run Code Online (Sandbox Code Playgroud)

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

我怎样才能做到这一点?

spa*_*ovv 236

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

client := http.Client{
    Timeout: 5 * time.Second,
}
client.Get(url)
Run Code Online (Sandbox Code Playgroud)

这对我来说已经成功了.

  • 嗯,这对我来说已经足够了.很高兴我向下滚动了一下:) (8认同)
  • 超时命中时会发生什么?"Get"会返回错误吗?我有点困惑,因为`Client`的Godoc说:_计时器在Get,Head,Post或Do返回后仍然运行,并且会中断对Response的反应.Body._这是否意味着_either_`Get`_or_阅读`Response.Body`可能被错误打断? (8认同)
  • 有没有办法让每个请求有不同的超时? (3认同)
  • 问题,“http.Client.Timeout”与“http.Transport.ResponseHeaderTimeout”有什么区别? (3认同)
  • @Roylee根据文档的主要区别之一:`http.Client.Timeout`包括读取响应体的时间,`http.Transport.ResponseHeaderTimeout`不包含它. (2认同)

Vol*_*ker 52

您需要使用自己的传输设置自己的客户端,该传输使用自定义拨号功能,该功能包裹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")
}
Run Code Online (Sandbox Code Playgroud)

  • @Daniel B:你问的是错误的问题.这不是关于优势,而是关于每个代码的作用.如果在已建立的连接上的某些操作花费太长时间,则在其他超时启动时,如果服务器无法处理,则DialTimeouts会跳入.如果您的目标服务器快速建立连接,然后开始慢速禁用,则拨号超时将无济于事. (4认同)
  • @Jonno:Go 中没有强制转换。这些是类型转换。 (2认同)

dmi*_*ael 30

要添加到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),
        },
    }
}
Run Code Online (Sandbox Code Playgroud)

此代码已经过测试并正在生产中.有关测试的完整要点,请访问 https://gist.github.com/dmichael/5710968

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

  • 此超时不适用于keepalive连接,这是默认设置,也是我想象的大多数人应该使用的连接.以下是我提出的解决这个问题的方法:http://gist.github.com/seantalts/11266762 (3认同)

zan*_*ngw 24

Go 模块中有几个客户端超时http,并且当前答案中有一些这些超时的示例。

这是一张图片来说明客户端超时,请参阅Go net/http 超时的完整指南 在此输入图像描述

有两种方法可以设置HTTP请求的超时时间

  • http.Client
client := http.Client{
    Timeout: 3 * time.Second,
}
resp, err := client.Do(req)
Run Code Online (Sandbox Code Playgroud)
  • 语境
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, URL)
Run Code Online (Sandbox Code Playgroud)

它们之间的区别

  • 使用上下文是特定于请求的,而使用客户端超时可能会应用于传递给Do客户端方法的所有请求。
  • 如果您想专门deadline/timeout处理每个请求,则使用上下文,否则,如果您希望每个出站请求有 1 个超时,则使用客户端超时就足够了。


Cha*_*ant 11

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

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))
Run Code Online (Sandbox Code Playgroud)

  • 应该是 Go 1.7+ 的**接受**答案。对于 Go 1.13+ 可以使用 [NewRequestWithContext](https://golang.org/pkg/net/http/#NewRequestWithContext) 稍微缩短 (4认同)
  • 额外信息:根据文档,Context 施加的截止日期还包括阅读正文,类似于“http.Client.Timeout”。 (2认同)

zzz*_*zzz 9

快速而肮脏的方式:

http.DefaultTransport.(*http.Transport).ResponseHeaderTimeout = time.Second * 45
Run Code Online (Sandbox Code Playgroud)

这是在没有任何协调的情况下改变全球状态.但是你的网址提取器可能还可以.否则创建一个私有实例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)
...
Run Code Online (Sandbox Code Playgroud)

以上没有测试过.