Golang'da http.Get () istekleri için zaman aşımı nasıl ayarlanır?


106

Go'da bir URL alıcısı yapıyorum ve alınacak URL'lerin bir listesi var. http.Get()Her URL'ye istek gönderiyorum ve yanıtlarını alıyorum .

resp,fetch_err := http.Get(url)

Her Alma isteği için nasıl özel bir zaman aşımı belirleyebilirim? (Varsayılan süre çok uzundur ve bu, alıcımı gerçekten yavaşlatır.) Alıcımın yaklaşık 40-45 saniyelik bir zaman aşımına sahip olmasını ve ardından "istek zaman aşımına uğradı" döndürüp sonraki URL'ye geçmesini istiyorum.

Bunu nasıl başarabilirim?


1
Sadece bu yolu daha uygun bulduğumu bilmenizi isterim (en azından benim için ağ sorunları varsa, çevirme zaman aşımı iyi çalışmaz): blog.golang.org/context
Audrius

@Audrius Ağ sorunları varken çevirme zaman aşımının neden çalışmadığına dair bir fikriniz var mı? Sanırım ben de aynı şeyi görüyorum. DialTimeout bunun için mi sanıyordum?!?!
Ürdün

@Jordan Kütüphane kodunun bu kadar derinlerine dalmadığım için söylemek zor. Çözümümü aşağıda yayınladım. Bunu bir süredir üretimde kullanıyorum ve şimdiye kadar "sadece çalışıyor" (tm).
Audrius

Yanıtlar:


255

Görünüşe göre Go 1.3'te http.Client Zaman Aşımı alanına sahip

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

Bu benim için hile yaptı.


10
Bu benim için yeterince iyi. Biraz aşağı kaydırdığıma sevindim :)
James Adam

5
İstek başına farklı bir zaman aşımına sahip olmanın bir yolu var mı?
Arnaud Rinquin

11
Zaman aşımı gerçekleştiğinde ne olur? Does Getbir hata döndürür? Biraz kafam karıştı çünkü Godoc Clientşöyle diyor: Get, Head, Post veya Do döndükten sonra zamanlayıcı çalışmaya devam ediyor ve Response.Body'nin okunmasını kesintiye uğratacak . Yani bu demek oluyor mu ya Get ya okuma Response.Bodybir hata sonucunda müdahale edilebilir?
Avi Flax

1
Soru, arasındaki fark nedir http.Client.Timeoutvs. http.Transport.ResponseHeaderTimeout?
Roy Lee

2
@ Roylee Dokümanlara göre temel farklılıklardan biri: http.Client.Timeoutyanıt gövdesini okuma süresini içerir, http.Transport.ResponseHeaderTimeoutiçermez.
imwill

53

DialTimeout'un etrafını saran özel bir Arama işlevi kullanan kendi Aktarıcınız ile kendi İstemcinizi kurmanız gerekir .

(Tamamen gibi bir şey denenmemiş ) bu :

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

Çok teşekkürler! Bu tam olarak aradığım şey.
2013

net.DialTimeout'u zzzz'in cevabında açıklanan Transport.ResponseHeaderTimeout'a göre kullanmanın avantajı nedir?
Daniele B

4
@Daniel B: Yanlış soruyu soruyorsunuz. Avantajlarla ilgili değil, her kodun ne yaptığı ile ilgilidir. DialTimeoutlar, sunucuda program yapılamıyorsa, diğer zaman aşımları başlarsa, kurulan bağlantıdaki belirli işlemler çok uzun sürerse devreye girer. Hedef sunucularınız hızlı bir şekilde bağlantı kurar ancak daha sonra sizi yavaşlatmaya başlarsa, çevirme zaman aşımı yardımcı olmaz.
Volker

1
@Volker, cevabınız için teşekkürler. Aslında ben de fark ettim: Transport.ResponseHeaderTimeout okuma zaman aşımını ayarlıyor gibi görünüyor, yani bağlantı kurulduktan sonraki zaman aşımı, sizin çevirme zaman aşımıyken. Dmichael'in çözümü hem çevirme zaman aşımı hem de okuma zaman aşımı ile ilgilidir.
Daniele B

1
@Jonno: Go'da yayın yok. Bunlar tip dönüşümleridir.
Volker

31

Volker'ın cevabına eklemek için, bağlantı zaman aşımına ek olarak okuma / yazma zaman aşımını da ayarlamak isterseniz, aşağıdaki gibi bir şey yapabilirsiniz.

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

Bu kod test edildi ve üretimde çalışıyor. Testlerin tam özeti burada mevcuttur https://gist.github.com/dmichael/5710968

conn.SetDeadlineGelecekte bir noktaya atıfta bulunduğundan, her istek için yeni bir müşteri oluşturmanız gerekeceğini unutmayın.time.Now()


Conn.SetDeadline'ın dönüş değerini kontrol etmeniz gerekmez mi?
Eric Urban

3
Bu zaman aşımı, canlı tutma bağlantıları ile çalışmıyor, bu varsayılan ve çoğu insanın hayal ettiğim gibi kullanması gerekiyor. İşte bununla başa çıkmak için bulduğum
şey

Ek girdiler için @xitrium ve Eric'e teşekkürler.
dmichael

Her istek için yeni bir müşteri oluşturmamız gerekeceğini söylediğin gibi hissediyorum. Dial, her seferinde arandığını düşündüğüm bir işlev olduğundan, her isteği aynı istemciye gönderiyorsunuz.
A-letubby

Her seferinde yeni bir müşteriye ihtiyacınız olduğuna emin misiniz? Her çevirişinde net.Dial kullanmak yerine, TimeoutDialer'ın oluşturduğu işlevi kullanır. Bu, her seferinde yeni bir zamanda değerlendirilen son tarih ile yeni bir bağlantı. Şimdi () çağrısı.
Blake Caldwell

16

Bunu istek başına yapmak istiyorsanız, hata işleme kısalık için göz ardı edilir:

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)

1
Ekstra bilgi: Belge başına, Bağlam tarafından belirlenen son tarih ayrıca http.Client.Timeout.
kubanczyk

1
Go 1.7+ için kabul edilen bir yanıt olmalıdır . Git 1.13+ hafifçe kullanılarak kısaltılabilir için NewRequestWithContext
Kubanczyk

9

Hızlı ve kirli bir yol:

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

Bu, herhangi bir koordinasyon olmaksızın küresel durumu değiştiriyor. Yine de muhtemelen url alıcınız için uygun olabilir. Aksi takdirde şunun özel bir örneğini oluşturun 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)
...

Yukarıdaki hiçbir şey test edilmedi.


Lütfen birisi beni düzeltir, ancak ResponseHeaderTimeout okuma zaman aşımı ile ilgili, yani bağlantı kurulduktan sonraki zaman aşımı gibi görünüyor. En kapsamlı çözüm, hem çevirme zaman aşımını hem de okuma zaman aşımını ayarlamaya izin verdiği için @dmichael tarafından verilen çözüm gibi görünüyor.
Daniele B

http.DefaultTransport.(*http.Transport).ResponseHeaderTimeout = time.Second * 45istek zaman aşımı için yazma testinde bana çok yardımcı ol. Çok teşekkür ederim.
lee


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

Bu yardımcı olabilir, ancak bunun ResponseHeaderTimeoutyalnızca bağlantı kurulduktan sonra başladığını unutmayın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.