Cevabı kapatmazsam ne olabilir? Vücut?


98

Go'da bazı http yanıtlarım var ve bazen aramayı unutuyorum:

resp.Body.Close()

Bu durumda ne olur? hafıza sızıntısı olacak mı? Ayrıca, defer resp.Body.Close()yanıt nesnesini aldıktan hemen sonra yerleştirmek güvenli midir?

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

Ne var bir hatadır, eğer olabilir respveya resp.Bodysıfır olarak?


Erteleme resp.Body.Close () 'yi hata! = Nil sonrasına koymak sorun değil çünkü hata sıfır olmadığında zaten kapanır. Öte yandan, istek başarılı olduğunda organın açıkça kapatılması gerekir.
Vasantha Ganesh K

Yanıtlar:


110

Bu durumda ne olur? hafıza sızıntısı olacak mı?

Bu bir kaynak sızıntısı. Bağlantı yeniden kullanılmaz ve açık kalabilir, bu durumda dosya tanımlayıcısı serbest bırakılmaz.

Ayrıca, yanıt nesnesini aldıktan hemen sonra defer resp.Body.Close () koymak güvenli midir?

Hayır, belgelerde verilen örneği izleyin ve hatayı kontrol ettikten hemen sonra kapatın.

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

Gönderen http.Clientbelgeler:

Döndürülen hata sıfır ise, Yanıt, kullanıcının kapatması beklenen sıfır olmayan bir Gövde içerecektir. Gövde hem EOF'ye okunmaz hem de kapatılmazsa, İstemcinin temelindeki RoundTripper (tipik olarak Taşıma), sonraki bir "canlı tutma" isteği için sunucuya kalıcı bir TCP bağlantısını yeniden kullanamayabilir.


4
Bu bağlantıya göre , kodunuzla bağlantıyı sızdırmanız hala mümkündür. Yanıtın sıfır olmadığı ve hatanın sıfır olmadığı bazı durumlar vardır.
mmcdole

13
@mmcdole: Bu gönderi sadece yanlış ve bir hatada döndürülen yanıtın tanımlanmış bir durumu olmadığı için paniğe kapılmayacağının garantisi yok. Bir Gövde bir hata nedeniyle kapatılmadıysa, bu bir hatadır ve rapor edilmesi gerekir. Rastgele bir blog yazısı yerine "Hata durumunda herhangi bir Yanıt göz ardı edilebilir" yazan resmi istemci belgelerine bakmalısınız .
JimB

2
@ del-boy: Eğer istemcinin daha fazla istekte bulunmasını bekliyorsanız, bağlantının yeniden kullanılabilmesi için gövdeyi okumaya çalışmalısınız. Bağlantıya ihtiyacınız yoksa, cesedi okumaya zahmet etmeyin. Cesedi okursanız, sarın io.LimitReader. İstek çok büyükse yeni bir bağlantı kurmak daha hızlı olduğu için genellikle oldukça küçük bir limit kullanırım.
JimB

1
Bunu yapmak _, err := client.Do(req)aynı zamanda dosya tanımlayıcının açık kalmasına neden olur. Yani yanıtın ne olduğu umurunda olmasa bile, yine de bir değişkene atamak ve vücudu kapatmak gerekir.
j boschiero

1
İlgilenen herkes için dokümantasyonun tamamı şu şekildedir (vurgu eklenmiştir): "Hata durumunda herhangi bir Yanıt göz ardı edilebilir. Sıfır olmayan bir hata ile sıfır olmayan bir Yanıt yalnızca CheckRedirect başarısız olduğunda ve hatta o zaman döndürülen Yanıt olduğunda oluşur. kapalı."
nishanthshanmugham

15

Eğer Response.Bodykapalı olmayacak Close()bir fd ile ilişkili bir kaynak serbest edilmeyecektir daha yöntemle. Bu bir kaynak sızıntısıdır.

Kapanış Response.Body

Gönderen tepki kaynağı :

Bedeni kapatmak arayanın sorumluluğundadır.

Dolayısıyla, nesneye bağlı sonlandırıcı yoktur ve açıkça kapatılmalıdır.

Hata işleme ve ertelenen temizlik

Hata durumunda herhangi bir Yanıt göz ardı edilebilir. Sıfır olmayan bir hata içeren sıfır olmayan bir Yanıt yalnızca CheckRedirect başarısız olduğunda ve bu durumda bile döndürülen Response.Body zaten kapalı olduğunda oluşur.

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
Hata işleme koşulunuz dahilinde geri dönmeleri gerektiğini unutmamalısınız. Bu, kullanıcı hata işlemede geri dönmezse paniğe neden olacaktır.
applewood

3

Tanımlayıcı, yukarıda bahsedildiği gibi ilk başta asla kapanmaz.

Ve dahası, golang, eğer yanlışsa, bağlantıyı persistConnyeniden kullanmak için (sarmalamak için struct kullanarak) önbelleğe alır DisableKeepAlives.

Kullandıktan sonra golang'da client.Dogo, adımlardan readLoopbiri olarak gorutin yöntemini çalıştıracaktır .

Böylece golang http transport.gobir pconn(persistConn struct)koymak olmayacak idleConniptal req kadar kanal readLoop, aynı zamanda yöntem ve bu goroutine ( readLoopiptal req kadar engellenir yöntemi).

İşte onu gösteren kod .

Daha fazlasını öğrenmek istiyorsanız, readLoopyöntemi görmeniz gerekir .


1

Bkz. Https://golang.org/src/net/http/client.go
"Hata sıfır olduğunda, resp her zaman sıfır olmayan bir yanıt içerir."

ama hata olduğunda! = nil, resp her zaman sıfırdır demezler.
Şöyle devam ederler: "Eğer ilgili gövde kapatılmamışsa, İstemcinin temelindeki RoundTripper (tipik olarak Transport), sonraki bir" canlı tutma "isteği için sunucuya kalıcı bir TCP bağlantısını yeniden kullanamayabilir."

Bu yüzden sorunu genellikle şu şekilde çözdüm:

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

3
Bu yanlıştır ve bir hata olduğunda vücudun sıfır olacağının garantisi yoktur.
JimB

1
Teşekkürler @ JimB. Dokümanlardaki ifade şu şekildedir: "Hata durumunda herhangi bir Yanıt yok sayılabilir. "Hata durumunda, Vücut her zaman kapalıdır." Demek daha doğru olur.
candita

1
Hayır, çünkü genellikle kapatılacak bir müdahale organı yoktur. Dokümanlarda bu paragrafı okumaya devam ederseniz - "Sıfır olmayan bir hata ile sıfır olmayan bir yanıt yalnızca CheckRedirect başarısız olduğunda ve hatta döndürülen Response.Body zaten kapalı olduğunda oluşur."
JimB
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.