Що може статися, якщо я не закрию відповідь. Тіло?


98

У Go у мене є відповіді на 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нульовою?


Це добре, щоб відкласти resp.Body.Close () після err! = Nil, коли присутнє повернення, оскільки, коли помилка не рівна nil, вона вже закрита. З іншого боку, тіло потрібно явно закрити, коли запит вдається.
Васанта Ганеш К

Відповіді:


110

Що відбувається в цьому випадку? чи буде витік пам'яті?

Це витік ресурсів. З’єднання не використовуватиметься повторно і може залишатися відкритим, і в цьому випадку дескриптор файлу не буде звільнений.

Крім того, чи безпечно розміщувати дефект resp.Body.Close () відразу після отримання об'єкта відповіді?

Ні, дотримуйтесь прикладу, наведеного в документації, і закрийте його одразу після перевірки помилки.

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

З http.Clientдокументації:

Якщо повернута помилка дорівнює нулю, відповідь міститиме не нульове тіло, яке користувач повинен закрити. Якщо тіло одночасно не зчитується в EOF і не закривається, основний RoundTripper Клієнта (як правило, транспортний) може не змогти повторно використовувати постійне TCP-з'єднання з сервером для подальшого запиту "підтримувати живий".


4
Згідно з цим посиланням , все ще можна отримати зв’язок із вашим кодом. Є деякі випадки, коли відповідь не нульова, а помилка не нульова.
mmcdole

13
@mmcdole: Цей допис просто неправильний, і немає гарантії, що він не буде панікувати, оскільки будь-яка відповідь, що повертається на помилку, не має визначеного стану. Якщо тіло не закрито через помилку, тоді це помилка, про яку потрібно повідомити. Вам слід скористатися офіційною документацією клієнта , в якій зазначено "При помилці будь-яку відповідь можна ігнорувати", а не випадковою публікацією в блозі.
JimB

2
@ del-boy: Якщо ви очікуєте, що клієнт зробить більше запитів, спробуйте прочитати тіло, щоб зв’язок можна було використовувати повторно. Якщо вам не потрібен зв’язок, то не турбуйтеся читанням тіла. Якщо ви читаєте тіло, оберніть його io.LimitReader. Зазвичай я використовую досить невеликий ліміт, оскільки швидше встановити нове з'єднання, якщо запит занадто великий.
JimB

1
Варто зазначити, що це _, err := client.Do(req)також призводить до того, що дескриптор файлу залишається відкритим. Отже, навіть якщо людині все одно, яка реакція, все одно необхідно призначити її змінній і закрити тіло.
j boschiero

1
Для всіх, хто цікавиться, подається повна документація (курсив додано): "При помилці будь-яку відповідь можна ігнорувати. Ненульова відповідь із ненульовою помилкою виникає лише тоді, коли CheckRedirect не працює, і навіть тоді повернутий Response.Body вже є зачинено."
nishanthshanmugham

15

Якщо Response.Bodyне буде закрито Close()методом, ресурси, пов'язані з fd, не звільняться. Це витік ресурсів.

Закриття Response.Body

Від джерела відповіді :

Закрити Орган відповідає той, хто телефонує.

Отже, немає фіналізаторів, прив’язаних до об’єкта, і його потрібно явно закрити.

Обробка помилок та відстрочка очищення

При помилці будь-яку відповідь можна проігнорувати. Ненульова відповідь із помилкою, що не відповідає нулю, виникає лише тоді, коли CheckRedirect не працює, і навіть тоді повернутий орган відповіді вже закритий.

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
Зверніть увагу, що вони повинні повернутися в межах вашого стану обробки помилок. Це спричинить паніку, якщо користувач не повернеться до обробки помилок.
applewood

3

Спочатку дескриптор ніколи не закривається, як це було згадано вище.

Більше того, golang кешує з’єднання (використовуючи persistConnstruct для обгортання) для його повторного використання, якщо DisableKeepAlivesвоно хибне.

У методі golang після використання client.Dogo виконує readLoopметод goroutine як один із кроків.

Отже, в golang http transport.go, a pconn(persistConn struct)не буде введено в idleConnканал, поки вимогу не буде скасовано в readLoopметоді, а також цей goroutine ( readLoopметод) буде заблокований до тих пір, поки не буде скасовано.

Ось код, який його показує.

Якщо ви хочете дізнатися більше, вам потрібно ознайомитися з readLoopметодом.


1

Див. Https://golang.org/src/net/http/client.go
"Коли помилка дорівнює нулю, resp завжди містить не нульовий resp.Body."

але вони не говорять, коли помилка! = нуль, відповідно завжди нуль. Вони продовжують говорити:
"Якщо resp.Body не закрито, основний RoundTripper Клієнта (як правило, Transport) може не мати змоги повторно використовувати постійне TCP-з'єднання з сервером для подальшого запиту" підтримувати живий "."

Тож я зазвичай вирішував проблему так:

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

3
Це неправильно, і немає жодної гарантії, що відповідно.
JimB 02

1
Дякую @JimB. Формулювання в документації: "При помилці будь-яку відповідь можна ігнорувати". Точніше було б сказати "На помилку тіло відповіді завжди закрите".
candita

1
Ні, оскільки зазвичай не існує органу реагування, який слід закрити. Якщо ви продовжуєте читати цей абзац у документах - "Ненульова відповідь із помилкою, що не відповідає нулю, виникає лише тоді, коли CheckRedirect не вдається, і навіть тоді повернутий Response.Body вже закритий."
JimB
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.