Что может случиться, если я не закрываю ответ. В голанге?
В golang у меня есть несколько ответов HTTP, и я иногда забываю позвонить:
resp.Body.Close()
Что происходит в этом случае? будет ли утечка памяти? Также безопасно ли вставлять defer resp.Body.Close()
сразу после получения объекта ответа?
client := http.DefaultClient
resp, err := client.Do(req)
defer resp.Body.Close()
if err != nil {
return nil, err
}
Что делать, если есть ошибка, может ли resp
или resp.Body
быть nil?
Ответы
Ответ 1
Что происходит в этом случае? будет ли утечка памяти?
Это утечка ресурса. Соединение не будет использоваться повторно и может оставаться открытым, в этом случае дескриптор файла не будет освобожден.
Также безопасно помещать в defer resp.Body.Close() сразу после получения объекта ответа?
Нет, следуйте примеру, приведенному в документации, и закройте его сразу после проверки ошибки.
client := http.DefaultClient
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
Из документации http.Client
:
Если возвращенная ошибка равна nil, Ответ будет содержать не ноль Тело, которое пользователь должен закрыть. Если тело не одновременно считывается в EOF и не закрывается, клиент, лежащий в основе RoundTripper (обычно Transport), возможно, не сможет повторно использовать постоянное TCP-соединение с сервером для последующего запроса "keep-alive".
Ответ 2
Если Response.Body
не будет закрыт методом Close()
, ресурсы, связанные с fd, не будут освобождены. Это утечка ресурсов.
Закрытие Response.Body
Из источника ответа:
Это ответственность вызывающего абонента, чтобы закрыть тело.
Таким образом, к объекту не привязаны финализаторы, и он должен быть явно закрыт.
Обработка ошибок и отложенные очистки
В случае ошибки любой ответ может быть проигнорирован. Ответ, отличный от nil, с ошибкой, отличной от nil, возникает только в случае сбоя CheckRedirect, и даже тогда возвращенный Response.Body уже закрыт.
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
Ответ 3
См. https://golang.org/src/net/http/client.go
"Когда err is nil, resp всегда содержит non-nil resp.Body."
но они не говорят, когда err!= nil, resp всегда равно nil. Они продолжают говорить:
"Если resp.Body не закрыт, Клиент, лежащий в основе RoundTripper (обычно Transport), может не иметь возможности повторно использовать постоянное TCP-соединение с сервером для последующего запроса" keep-alive "".
Поэтому я, как правило, решил проблему следующим образом:
client := http.DefaultClient
resp, err := client.Do(req)
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
return nil, err
}
Ответ 4
Сначала дескриптор никогда не закрывается, как указано выше.
И что еще, golang будет кэшировать соединение (используя persistConn
struct to wrap) для повторного использования, если DisableKeepAlives
- false.
В golang после использования метода client.Do
go будет запускать goroutine readLoop
как один из шагов.
Итак, в golang http transport.go
, pconn(persistConn struct)
не будет помещаться в канал idleConn
, пока req не будет отменен в методе readLoop
, а также этот goroutine (метод readLoop
) будет заблокирован до тех пор, пока req отменен.
Вот код, показывающий его.
Если вы хотите узнать больше, вам нужно увидеть метод readLoop
.