2014-05-31 8 views
5
tr := &http.Transport{ 
    TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 
} 
client := &http.Client{Transport: tr} 
response, err := client.Get(link) 
if err != nil { 
    fmt.Println(err) 
} 
defer response.Body.Close() 

//block forever at the next line 
content, _ = ioutil.ReadAll(response.Body) 

Powyższy kod jest przeznaczony do odczytu treści ze strony internetowej, która znajduje się w pętli. Zauważyłem, że czasami linia ioutil.ReadAll(response.Body) będzie blokować na zawsze. Dzieje się tak losowo, jednak prawie zawsze dzieje się na tej stronie: http://xkcd.com/55. To bardzo interesujące, że gdy robię curl http://xkcd.com/55, to nic nie zwraca, jednak wget http://xkcd.com/55 zwraca całą stronę.ioutil.ReadAll (response.Body) blokuje na zawsze - Golang

+1

Może są przekierowania: zobacz moją funkcję pobierania, aby śledzić te, z JarCookie zawarte: https://github.com/VonC/senvgo/blob/bf74db02b675bb36e0213bfdc68d6750c5bf944f/main.go#L19 29-L1979 – VonC

+1

Właśnie przetestowałem pobieranie http://xkcd.com/55 i działa dobrze (z moją wersją kodu http) – VonC

+0

Dzięki @VonC spróbuję. –

Odpowiedz

1

Twój kod powinien działać zgodnie z oczekiwaniami. Zgaduję, to jest problem z siecią. Spróbuj ustawić wyższy limit czasu.

package main 

import (
    "crypto/tls" 
    "fmt" 
    "io/ioutil" 
    "net/http" 
) 

func main() { 

    link := "http://xkcd.com/55" 

    tr := &http.Transport{ 
     TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 
    } 
    client := &http.Client{Transport: tr} 
    response, err := client.Get(link) 
    if err != nil { 
     fmt.Println(err) 
    } 
    defer response.Body.Close() 

    //block forever at the next line 
    content, _ := ioutil.ReadAll(response.Body) 

    fmt.Println(string(content)) 

} 
5

Podejrzewam problem jest to, że starają się odczytać treść odpowiedzi, nawet jeśli wystąpi błąd:

if err != nil { 
    fmt.Println(err) 
} 

Należy też mieć else po tym, czy zalecana return lub continue lub coś jeszcze. Twoja linia ReadAll() jest niezdefiniowana.

(Jeśli pierwotnie skopiowane to z przykładowym kodem Get(), trzeba pamiętać, że zawiera log.Fatalf() w nodze błąd, który kończy program.)

Podejrzewam, że jak mówisz, czasami są coraz błąd sieciowy z tego czy innego powodu. Czy sprawdzasz dane wyjściowe dla wyniku Println()? W sposób, w jaki to zrobiłeś, mogłem sobie wyobrazić, że łatwo się w nim zakopuje.

Jak @twotwotwo zauważa, ten URL zwraca przekierowanie do tego samego adresu URL z końcowym ukośnikiem. Get() automatycznie obsłuży to za Ciebie, więc to nie jest problem. Domyślnie curl nie śledzi przekierowań podczas gdy wget robi. Możesz zobaczyć informacje o nagłówku, przekazując -i, aby zawinąć.


Inne rzeczy do sprawdzenia:

  • Upewnij się defer jest faktycznie nazywa. Pamiętaj, że defer jest wywoływane na końcu funkcji, a nie na końcu bieżącego zakresu. Więc jeśli jesteś w pętli (jak wspomniałeś), po prostu zgromadzisz bloki defer i nigdy nie zamkniesz tych odpowiedzi.

  • Jeśli serwer nigdy nie zamknie połączenia, io.ReadAll() nigdy nie wróci. To jest funkcja. Jeśli chcesz czas oczekiwania, musisz handle that yourself. Powinieneś być w stanie przetestować tę hipotezę za pomocą narzędzi takich jak curl. W niektórych rozwiązaniach, zobacz:

+0

Dzięki @Rob, tak, to dobry punkt. Włożyłem dużo kłody i okazało się, że nie doszło tutaj do błędu. Jednak rzeczywiście zablokowane w ioutil.ReadAll (response.Body). Co ciekawe, nigdy nie blokuje się na pierwsze żądanie. Będzie prawie zawsze blokować na drugiej lub trzeciej prośbie. Wygląda na to, że gdy serwer wykrył wiele żądań w krótkim czasie, serwer traktuje drugie z trzecich połączeń przychodzących inaczej niż pierwsze. Rozumiem, że serwer może użyć pewnej strategii, aby chronić go przed oszustwami. Mam jednak nadzieję, że również nie będzie blokować na zawsze. –

0

Chyba znalazłem rozwiązanie dodając DisableKeepAlives: true, do `& http.Transport, tak:

tr := &http.Transport{ 
    TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 
    DisableKeepAlives: true, 
} 

Ponieważ dokonałem tej zmiany, nie spotkałem jeszcze żadnego długiego blokowania. Ale nie jestem w 100% pewien, czy to jest rozwiązanie. Zostawię nowy kod na jeden dzień lub dwa. Jeśli nie będzie blokowania, myślę, że ten problem został rozwiązany.

+0

Jak dotąd nie blokuje się jeszcze. Jednak najdłuższe oczekiwanie trwało około 15 minut. Zastanawiam się, jak ustawić limit czasu do 'ioutil.ReadAll (response.Body)'. –

+0

Okazało się, że to nie jest rozwiązanie. Znowu blokuje na zawsze. Wygląda na to, nadal potrzebuję znaleźć sposób ustawić limit czasu w tej linii 'ioutil.ReadAll (response.Body)'. –

4

Dodatkowo uniknąć odczytać reakcja na ciało w ReadAll bez kontroli pamięci limitów/buforowe, na przykład:

googleResponse := GoogleResponse{} 
err = json.NewDecoder(io.LimitReader(resp.Body, MAX_MEMORY)).Decode(&googleResponse) 
if err != nil { 
    return nil, err 
} 

Więcej o nim w dobrych blogach:
Crossing Streams: a Love Letter to io.Reader by Jason Moiron
ioutil.ReadAll(httpResponse.Body) memory consumption
Golang Slices And The Case Of The Missing Memory