Ответ 1
Видимо в Go 1.3 http.Client имеет поле Timeout
client := http.Client{
Timeout: 5 * time.Second,
}
client.Get(url)
Это помогло мне.
Я делаю сборщик URL в Go и имею список URL-адресов для извлечения. Я отправляю http.Get()
запросы на каждый URL-адрес и получаю их ответ.
resp,fetch_err := http.Get(url)
Как установить индивидуальный тайм-аут для каждого запроса Get? (Время по умолчанию очень длинное, и это делает мой выборщик очень медленным.) Я хочу, чтобы у моего получателя было время ожидания около 40-45 секунд, после чего оно должно было вернуть "запрошенный тайм-аут" и перейти к следующему URL-адресу.
Как я могу это достичь?
Видимо в Go 1.3 http.Client имеет поле Timeout
client := http.Client{
Timeout: 5 * time.Second,
}
client.Get(url)
Это помогло мне.
Вам нужно настроить Client свой собственный Transport, который использует пользовательская функция набора, которая обтекает DialTimeout.
Что-то вроде (полностью непроверено) this:
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, если вы также хотите установить тайм-аут чтения/записи в дополнение к тайм-ауту соединения, вы можете сделать что-то вроде следующего
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()
Если вы хотите сделать это для запроса, для краткости ошибочная обработка игнорируется:
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))
Быстрый и грязный способ:
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)
...
Ничего выше не было проверено.
Вы можете использовать https://github.com/franela/goreq, который обрабатывает таймауты способом и простым способом.
В конце я закончил использование этой функции утилиты для всех запросов, требующих тайм-аута. По некоторым причинам код @sparrovv действовал.
// reqType is one of HTTP request strings (GET, POST, PUT, DELETE, etc.)
func DoRequest(reqType string, url string, headers map[string]string, data []byte, timeoutSeconds int) (int, []byte, map[string][]string, error) {
var reader io.Reader
if data != nil && len(data) > 0 {
reader = bytes.NewReader(data)
}
req, err := http.NewRequest(reqType, url, reader)
if err != nil {
return 0, nil, nil, err
}
// I strongly advise setting user agent as some servers ignore request without it
req.Header.Set("User-Agent", "YourUserAgentString")
if headers != nil {
for k, v := range headers {
req.Header.Set(k, v)
}
}
var (
statusCode int
body []byte
timeout time.Duration
ctx context.Context
cancel context.CancelFunc
header map[string][]string
)
timeout = time.Duration(time.Duration(timeoutSeconds) * time.Second)
ctx, cancel = context.WithTimeout(context.Background(), timeout)
defer cancel()
err = httpDo(ctx, req, func(resp *http.Response, err error) error {
if err != nil {
return err
}
defer resp.Body.Close()
body, _ = ioutil.ReadAll(resp.Body)
statusCode = resp.StatusCode
header = resp.Header
return nil
})
return statusCode, body, header, err
}
timeout := time.Duration(5 * time.Second)
transport := &http.Transport{Proxy: http.ProxyURL(proxyUrl), ResponseHeaderTimeout:timeout}
Это может помочь, но обратите внимание, что ResponseHeaderTimeout
запускается только после установления соединения.