W Go mam kilka odpowiedzi http i czasami zapominam zadzwonić:
resp.Body.Close()
Co się dzieje w tym przypadku? czy nastąpi wyciek pamięci? Czy jest też bezpieczne wprowadzenie defer resp.Body.Close()
natychmiast po otrzymaniu obiektu odpowiedzi?
client := http.DefaultClient
resp, err := client.Do(req)
defer resp.Body.Close()
if err != nil {
return nil, err
}
Co się stanie, jeśli wystąpi błąd, resp
czy może resp.Body
być zerowy?
Odpowiedzi:
To wyciek zasobów. Połączenie nie zostanie ponownie użyte i może pozostać otwarte, w takim przypadku deskryptor pliku nie zostanie zwolniony.
Nie, postępuj zgodnie z przykładem podanym w dokumentacji i zamknij go natychmiast po sprawdzeniu błędu.
client := http.DefaultClient resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close()
Z
http.Client
dokumentacji:źródło
io.LimitReader
. Zwykle używam dość małego limitu, ponieważ szybsze jest nawiązanie nowego połączenia, jeśli żądanie jest zbyt duże._, err := client.Do(req)
spowoduje to również pozostawienie otwartego deskryptora pliku. Więc nawet jeśli nie obchodzi nas, jaka jest odpowiedź, nadal konieczne jest przypisanie jej do zmiennej i zamknięcie ciała.Jeśli
Response.Body
nie zostanie zamknięteClose()
metodą, wówczas zasoby powiązane z fd nie zostaną zwolnione. To wyciek zasobów.Zamknięcie
Response.Body
Ze źródła odpowiedzi :
Nie ma więc finalizatorów związanych z obiektem i musi być jawnie zamknięty.
Obsługa błędów i odroczone czyszczenie
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
źródło
Początkowo deskryptor nigdy się nie zamyka, jak wspomniano powyżej.
Co więcej, golang buforuje połączenie (używając
persistConn
struct to wrap) do ponownego użycia, jeśliDisableKeepAlives
jest fałszywe.W golang po użyciu
client.Do
metody, go uruchomireadLoop
metodę goroutine jako jeden z kroków.Więc w golang http
transport.go
, apconn(persistConn struct)
nie zostanie wstawiony doidleConn
kanału, dopóki req nie zostanie anulowany wreadLoop
metodzie, a także ta goroutine (readLoop
metoda) zostanie zablokowana do czasu anulowania żądania .Oto kod, który to pokazuje.
Jeśli chcesz wiedzieć więcej, musisz zapoznać się z
readLoop
metodą.źródło
Zobacz https://golang.org/src/net/http/client.go
„Gdy błąd jest równy zero, resp zawsze zawiera wartość inną niż zero lub Body.”
ale nie mówią, kiedy err! = nil, resp zawsze jest nil.
Dalej mówią: „Jeśli odpowiednie ciało nie jest zamknięte, bazowy RoundTripper klienta (zazwyczaj Transport) może nie być w stanie ponownie użyć trwałego połączenia TCP z serwerem dla kolejnego żądania„ utrzymywania aktywności ”.
Więc zazwyczaj rozwiązałem problem w następujący sposób:
client := http.DefaultClient resp, err := client.Do(req) if resp != nil { defer resp.Body.Close() } if err != nil { return nil, err }
źródło