2014-05-31 11 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) 

È possibile che questo è il mio codice per leggere il contenuto da una pagina Web che risiede in un ciclo. Ho trovato a volte la linea ioutil.ReadAll(response.Body) si bloccherà per sempre. Ciò accade casualmente, tuttavia, si verifica quasi sempre in questa pagina Web: http://xkcd.com/55. È molto interessante che quando faccio curl http://xkcd.com/55, non restituisca nulla, tuttavia, wget http://xkcd.com/55 restituisce l'intera pagina web.ioutil.ReadAll (response.Body) blocca per sempre - Golang

+1

Forse sono redirect: vedi la mia funzione di download per seguire quelli, con un JarCookie incluso: https://github.com/VonC/senvgo/blob/bf74db02b675bb36e0213bfdc68d6750c5bf944f/main.go#L19 29-L1979 – VonC

+1

Ho appena provato a scaricare http://xkcd.com/55, e funziona perfettamente (con la mia versione del codice http) – VonC

+0

Grazie a @VonC darò una prova. –

risposta

1

Il codice dovrebbe funzionare come previsto. Sto indovinando, è un problema di rete. Prova a impostare un timeout più alto.

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

Ho il sospetto che il tuo problema è che si tenta di leggere il corpo della risposta, anche se c'è un errore:

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

Si dovrebbe sia avere un else dopo questo, o si dovrebbe return o continue o qualcosa altro. La tua riga ReadAll() non è definita.

(Se è stata originariamente copiata questo dal codice di esempio Get(), si noti che include un log.Fatalf() nella gamba errore, che termina il programma.)

Ho il sospetto che, come dici tu, di tanto in tanto si sta rilevando un errore di rete per una ragione o l'altra. Stai controllando l'output per il risultato dello Println()? Nel modo in cui l'hai fatto, potrei immaginarlo facilmente seppellito nell'output.

Come note @twotwotwo, questo URL restituisce un reindirizzamento allo stesso URL con una barra finale. Get() gestirà automaticamente questo per te, quindi non è questo il problema. Di default il ricciolo non segue i reindirizzamenti mentre wget lo fa. È possibile visualizzare le informazioni dell'intestazione passando a -i per arricciarsi.


Altre cose da verificare:

  • Assicurarsi che il defer è in realtà essere chiamato. Ricorda, defer viene chiamato alla fine della funzione, non alla fine dell'ambito corrente. Quindi, se ti trovi in ​​un ciclo (come dici tu), accumulerai solo blocchi defer e non chiuderai mai queste risposte.

  • Se il server non chiude mai la connessione, quindi io.ReadAll() non restituirà mai. Questa è una caratteristica. Se si desidera un timeout, è necessario handle that yourself. Dovresti essere in grado di testare questa ipotesi con strumenti come curl. Per alcune soluzioni, vedi:

+0

Grazie a @Rob, sì, è un buon punto. Ho messo un sacco di log e ho scoperto che l'errore non è successo qui. Tuttavia, è effettivamente bloccato su ioutil.ReadAll (response.Body). La cosa interessante è che non blocca mai alla prima richiesta. Quasi sempre bloccherà alla seconda o terza richiesta. Sembra che quando il server ha rilevato più richieste in breve tempo, il server tratta la seconda delle terze connessioni in entrata in modo diverso dal primo. Capisco perfettamente che il server possa utilizzare alcune strategie per proteggerlo dalle frodi. Tuttavia spero anche che non si blocchi per sempre. –

0

probabilmente ho trovato la soluzione con l'aggiunta DisableKeepAlives: true, al `& http.Trasporto, in questo modo:

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

Da quando ho apportato questa modifica, non ho ancora incontrato blocchi lunghi. Ma non sono sicuro al 100% che questa sia la soluzione. Lascerò il nuovo codice in esecuzione uno o due giorni. Se non ci saranno blocchi, penso che questo problema sia risolto.

+0

Finora non sta ancora bloccando. Tuttavia, l'attesa più lunga è stata di circa 15 minuti. Mi chiedo come impostare un timeout su 'ioutil.ReadAll (response.Body)'. –

+0

Si è scoperto che questa non è la soluzione. Blocca di nuovo per sempre. Sembra che debba ancora trovare un modo per impostare un timeout su questa riga 'ioutil.ReadAll (response.Body)'. –

4

Inoltre, evitare di leggere la risposta Corpo in ReadAll senza controllo/limiti di buffer di memoria, ad esempio:

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

Per saperne di più su di esso in buone post del blog:
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

Problemi correlati