Frage Wie setze ich das Timeout für http.Get () Anfragen in Golang?


Ich mache einen URL-Finder in Go und habe eine Liste von URLs, die abgerufen werden sollen. ich sende http.Get() Anfragen an jede URL und erhalten ihre Antwort.

resp,fetch_err := http.Get(url)

Wie kann ich ein benutzerdefiniertes Timeout für jede Get-Anforderung festlegen? (Die Standardzeit ist sehr lang und das macht meinen Fetcher wirklich langsam.) Ich möchte, dass mein Fetcher eine Zeitüberschreitung von etwa 40-45 Sekunden hat, nach der er "Anfrage abgelaufen" zurückgeben und mit der nächsten URL fortfahren soll.

Wie kann ich das erreichen?


74
2018-06-03 11:06


Ursprung


Antworten:


Anscheinend in Go 1.3 http.Client hat Timeout-Feld

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

Das ist der Trick für mich.


194
2017-08-16 22:10



Sie müssen Ihre eigenen einrichten Klient mit deinen eigenen Transport welches benutzt a benutzerdefinierte Dial-Funktion, die umschließt DialTimeout.

Etwas wie (vollständig ungetestet) Dies:

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")
}

52
2018-06-03 11:40



Wenn Sie zu Volkers Antwort hinzufügen möchten, dass Sie zusätzlich zum Verbindungszeitlimit auch das Lese- / Schreib-Timeout festlegen möchten, können Sie Folgendes tun

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),
        },
    }
}

Dieser Code ist getestet und funktioniert in der Produktion. Der vollständige Kern mit Tests ist hier verfügbar https://gist.github.com/dmichael/5710968

Beachten Sie, dass Sie für jede Anfrage einen neuen Client anlegen müssen conn.SetDeadline welches auf einen Punkt in der Zukunft verweist time.Now()


26
2018-06-05 02:33



Eine schnelle und schmutzige Art:

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

Dies mutiert den globalen Zustand ohne jegliche Koordination. Aber vielleicht ist es in Ordnung für Ihren URL-Finder. Andernfalls erstellen Sie eine private Instanz von 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)
...

Nichts darüber wurde getestet.


8
2018-06-03 11:36



Sie dürfen verwenden https://github.com/flanela/goreq die Timeouts auf einfache Art und Weise behandelt.


1
2018-01-21 19:42



Am Ende habe ich diese Dienstprogrammfunktion für alle Anfragen beendet, die eine Zeitüberschreitung erfordern. Aus irgendwelchen Gründen war der @sparrovv-Code in Ordnung.

// 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
}

0
2017-08-19 19:25