O que pode acontecer se eu não fechar a resposta. Corpo?

102

No Go, tenho algumas respostas http e às vezes me esqueço de ligar para:

resp.Body.Close()

O que acontece nesse caso? haverá um vazamento de memória? Também é seguro colocar defer resp.Body.Close()imediatamente após obter o objeto de resposta?

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

E se houver um erro, pode respou resp.Bodyser nulo?

Daniel Robinson
fonte
Tudo bem colocar adiar resp.Body.Close () depois de err! = Nulo quando o retorno está presente porque, quando o erro não é nulo, ele já vem fechado. Por outro lado, o corpo precisa ser fechado explicitamente quando a solicitação for bem-sucedida.
Vasantha Ganesh

Respostas:

114

O que acontece nesse caso? haverá um vazamento de memória?

É um vazamento de recursos. A conexão não será reutilizada e pode permanecer aberta, caso em que o descritor de arquivo não será liberado.

Também é seguro colocar em adiar resp.Body.Close () imediatamente após obter o objeto de resposta?

Não, siga o exemplo fornecido na documentação e feche-o imediatamente após verificar o erro.

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

Da http.Clientdocumentação:

Se o erro retornado for nulo, a resposta conterá um corpo diferente de nulo que o usuário deve fechar. Se o corpo não for lido para EOF e fechado, o RoundTripper subjacente do cliente (normalmente transporte) pode não ser capaz de reutilizar uma conexão TCP persistente com o servidor para uma solicitação "keep-alive" subsequente.

JimB
fonte
5
De acordo com isso link ainda é possível vazar a conexão com o seu código. Existem alguns casos em que a resposta não é nula e o erro não é nulo.
mmcdole
14
@mmcdole: Essa postagem está simplesmente errada e não há garantia de que não entre em pânico, já que qualquer resposta retornada em um erro não tem um estado definido. Se um corpo não for fechado devido a um erro, é um bug e precisa ser relatado. Você deve consultar a documentação oficial do cliente , que afirma "Em caso de erro, qualquer resposta pode ser ignorada", em vez de uma postagem de blog aleatória.
JimB de
2
@ del-boy: Se você espera que o cliente faça mais solicitações, tente ler o corpo para que a conexão possa ser reutilizada. Se você não precisa da conexão, não se preocupe em ler o corpo. Se você ler o corpo, embrulhe-o com io.LimitReader. Eu normalmente uso um limite bem pequeno, já que é mais rápido fazer uma nova conexão se a solicitação for muito grande.
JimB de
1
É importante ressaltar que fazer _, err := client.Do(req)também faz com que o descritor de arquivo permaneça aberto. Assim, mesmo que não se importe qual é a resposta, ainda é necessário atribuí-la a uma variável e fechar o corpo.
j boschiero
1
Para qualquer pessoa interessada, a documentação completa é (ênfase adicionada): "Em caso de erro, qualquer resposta pode ser ignorada. Uma resposta não nula com um erro não nulo ocorre apenas quando o CheckRedirect falha, e mesmo assim o Response.Body retornado já está fechadas."
nishanthshanmugham
15

Se Response.Bodynão for fechado comClose() método, os recursos associados a um fd não serão liberados. Este é um vazamento de recursos.

Fechando Response.Body

Da fonte de resposta :

É responsabilidade do chamador fechar o Body.

Portanto, não há finalizadores vinculados ao objeto e ele deve ser fechado explicitamente.

Tratamento de erros e limpezas adiadas

Em caso de erro, qualquer resposta pode ser ignorada. Uma Resposta não nula com um erro não nula ocorre apenas quando CheckRedirect falha e, mesmo então, o Response.Body retornado já está fechado.

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
I159
fonte
4
Você deve observar que eles devem retornar dentro da sua condição de tratamento de erros. Isso causará pânico se o usuário não retornar no tratamento de erros.
Applewood de
3

A princípio o descritor nunca fecha, como as coisas mencionadas acima.

E o que é mais, golang irá armazenar em cache a conexão (usando persistConnstruct para embrulhar) para reutilizá-la, se DisableKeepAlivesfor false.

No golang após o client.Dométodo de uso , go irá executar o goroutinereadLoop método como uma das etapas.

Portanto, em golang http transport.go, a pconn(persistConn struct)não será colocado no idleConncanal até que o req seja cancelado no readLoopmétodo, e também esta goroutine ( readLoopmétodo) será bloqueada até que o req seja cancelado.

Aqui está o código que o mostra.

Se você quiser saber mais, você precisa ver o readLoopmétodo.

zatrix
fonte
1

Veja https://golang.org/src/net/http/client.go
"Quando err é nulo, resp sempre contém um resp.Body não nulo."

mas eles não dizem quando err! = nulo, resp sempre é nulo. Eles continuam a dizer:
"Se resp.Body não estiver fechado, o RoundTripper subjacente do Cliente (normalmente Transporte) pode não ser capaz de reutilizar uma conexão TCP persistente com o servidor para uma solicitação" keep-alive "subsequente."

Então, normalmente resolvo o problema assim:

client := http.DefaultClient
resp, err := client.Do(req)
if resp != nil {
   defer resp.Body.Close()
}
if err != nil {
    return nil, err 
}
candita
fonte
3
Isso está incorreto e não há garantia de que resp.Body seja nit nil quando há um erro.
JimB
1
Obrigado @JimB. O texto nos documentos é "Em caso de erro, qualquer resposta pode ser ignorada." Seria mais correto dizer "Em caso de erro, o corpo de resposta está sempre fechado."
candita
1
Não, porque geralmente não há um corpo de resposta a ser fechado. Se você continuar lendo esse parágrafo nos documentos - "Uma resposta não nula com um erro não nula só ocorre quando o CheckRedirect falha e, mesmo assim, o Response.Body retornado já está fechado."
JimB