Cosa potrebbe accadere se non chiudo la risposta.


98

In Go, ho alcune risposte http e talvolta mi dimentico di chiamare:

resp.Body.Close()

Cosa succede in questo caso? ci sarà una perdita di memoria? Inoltre è sicuro da inserire defer resp.Body.Close()subito dopo aver ottenuto l'oggetto risposta?

client := http.DefaultClient
resp, err := client.Do(req)
defer resp.Body.Close()
if err != nil {
    return nil, err
}

E se c'è un errore, potrebbe respo resp.Bodyessere nullo?


Va bene mettere defer resp.Body.Close () dopo l'errore! = Nil quando è presente return perché, quando l'errore non è nullo, viene già chiuso. D'altra parte il corpo deve chiudersi esplicitamente quando la richiesta ha successo.
Vasantha Ganesh K

Risposte:


110

Cosa succede in questo caso? ci sarà una perdita di memoria?

È una perdita di risorse. La connessione non verrà riutilizzata e può rimanere aperta, nel qual caso il descrittore di file non verrà liberato.

Inoltre è sicuro mettere defer resp.Body.Close () immediatamente dopo aver ottenuto l'oggetto risposta?

No, segui l'esempio fornito nella documentazione e chiudilo subito dopo aver verificato l'errore.

client := http.DefaultClient
resp, err := client.Do(req)
if err != nil {
    return nil, err
}
defer resp.Body.Close()

Dalla http.Clientdocumentazione:

Se l'errore restituito è nullo, la risposta conterrà un corpo non nullo che l'utente dovrebbe chiudere. Se il corpo non viene letto su EOF e chiuso, il RoundTripper sottostante del client (in genere Transport) potrebbe non essere in grado di riutilizzare una connessione TCP persistente al server per una successiva richiesta "keep-alive".


4
Secondo questo link è ancora possibile perdere la connessione con il tuo codice. Ci sono alcuni casi in cui la risposta è diversa da zero e l'errore è diverso da zero.
mmcdole

13
@mmcdole: Quel post è semplicemente sbagliato e non c'è garanzia che non andrà in panico, dal momento che qualunque risposta viene restituita su un errore non ha uno stato definito. Se un corpo non viene chiuso a causa di un errore, allora è un bug e deve essere segnalato. Dovresti consultare la documentazione ufficiale del client , che afferma "In caso di errore, qualsiasi risposta può essere ignorata", piuttosto che un post sul blog casuale.
JimB

2
@ del-boy: Se ti aspetti che il client faccia più richieste, dovresti provare a leggere il corpo in modo che la connessione possa essere riutilizzata. Se non hai bisogno della connessione, non preoccuparti di leggere il corpo. Se leggi il corpo, avvolgilo con io.LimitReader. Di solito utilizzo un limite abbastanza piccolo, poiché è più veloce stabilire una nuova connessione se la richiesta è troppo grande.
JimB

1
Vale la pena sottolineare che facendo _, err := client.Do(req)anche il descrittore di file rimane aperto. Quindi, anche se non ci interessa quale sia la risposta, è comunque necessario assegnarla a una variabile e chiudere il corpo.
j boschiero

1
Per chiunque sia interessato, la documentazione completa è (enfasi aggiunta): "In caso di errore, qualsiasi risposta può essere ignorata. Una risposta diversa da zero con un errore non nullo si verifica solo quando CheckRedirect non riesce, e anche in questo caso la risposta restituita.Body è già chiuso."
nishanthshanmugham

15

Se Response.Bodynon verrà chiuso con Close()metodo, una risorsa associata a un fd non verrà liberata. Questa è una perdita di risorse.

Chiusura Response.Body

Dalla fonte della risposta :

È responsabilità del chiamante chiudere Body.

Quindi non ci sono finalizzatori associati all'oggetto e deve essere chiuso esplicitamente.

Gestione degli errori e pulizie differite

In caso di errore, qualsiasi risposta può essere ignorata. Una risposta non nullo con un errore non nullo si verifica solo quando CheckRedirect non riesce e anche in questo caso il Response.Body restituito è già chiuso.

resp, err := http.Get("http://example.com/")
if err != nil {
    // Handle error if error is non-nil
}
defer resp.Body.Close() // Close body only if response non-nil

4
Tieni presente che dovrebbero tornare nelle condizioni di gestione degli errori. Ciò causerà un panico se l'utente non torna nella gestione degli errori.
applewood

3

All'inizio il descrittore non si chiude mai, come le cose menzionate sopra.

E per di più, golang memorizzerà nella cache la connessione (usando la persistConnstruttura per avvolgere) per riutilizzarla, se DisableKeepAlivesè falso.

Nel client.Dometodo golang dopo l'uso , go eseguirà il readLoopmetodo goroutine come uno dei passaggi.

Quindi in golang http transport.go, a pconn(persistConn struct)non verrà inserito nel idleConncanale fino a quando il req non sarà cancellato nel readLoopmetodo, e anche questo goroutine ( readLoopmetodo) sarà bloccato fino a quando il req non sarà cancellato.

Ecco il codice che lo mostra.

Se vuoi saperne di più, devi vedere il readLoopmetodo.


1

Vedere https://golang.org/src/net/http/client.go
"Quando err è nil, resp contiene sempre un resp.Body non nullo."

ma non dicono quando err! = nil, resp è sempre nullo. Continuano a dire:
"Se resp.Body non è chiuso, il RoundTripper sottostante del client (tipicamente Transport) potrebbe non essere in grado di riutilizzare una connessione TCP persistente al server per una successiva richiesta" keep-alive "."

Quindi in genere ho risolto il problema in questo modo:

client := http.DefaultClient
resp, err := client.Do(req)
if resp != nil {
   defer resp.Body.Close()
}
if err != nil {
    return nil, err 
}

3
Questo non è corretto e non vi è alcuna garanzia che il rispettivo corpo sia nullo in caso di errore.
JimB

1
Grazie @JimB. La dicitura nei documenti è "In caso di errore, qualsiasi risposta può essere ignorata". Sarebbe più preciso dire "In caso di errore, il corpo della risposta è sempre chiuso".
candita

1
No, perché di solito non c'è un corpo di risposta da chiudere. Se continui a leggere quel paragrafo nella documentazione: "Una risposta non nullo con un errore non nullo si verifica solo quando CheckRedirect fallisce, e anche allora il Response.Body restituito è già chiuso."
JimB
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.