2015-09-08 2 views
-1

Так что я пытаюсь получить тело ответа из http.Response, чтобы сделать некоторые манипуляции, а затем установить его обратно. Это моя первая попытка получить его без слива http.Response:http.Response.Body копирование приводит к увеличению использования большой памяти

bodyBytes, err := ioutil.ReadAll(resp.Body) 
if err != nil { 
    // err 
} else { 
    cachedBody := string(bodyBytes) 

    // Restore the io.ReadCloser to its original state 
    // This is causing huge increases in memory usage. 
    resp.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) 
} 

Если я пошлю 500 запросов через, с размером ответа 1mb (большой кусок JSON, но это может быть любой формат, не только JSON), использование памяти сервера достигает ~ 400 Мбайт и не возвращается. Это нормально? Что-то не так с вышеуказанным кодом? Я что-то упустил, чтобы выпустить память?

defer resp.Body.Close() не имеет никакого эффекта.

Спасибо!


Edit 1

Вот суть с простой версией прокси, включая предложенный волосок. Если сервер на localhost: 3000 (где он проксирует) возвращает большой ответ, использование памяти будет быстро увеличиваться. В моем случае я возвращаю файл 1mb json и отправляю 500 запросов через прокси-сервер, увеличивая объем использования памяти до 400 МБ.

ходу прокси:
https://gist.github.com/marbemac/300c4c376171cbd27cc3

Простой сервер узла, который возвращает файл в том же каталоге под названием large_response.json.
https://gist.github.com/marbemac/55aaa78f5858484d33f6

+0

Возможно, это поможет вам http://stackoverflow.com/questions/21080642/memory-leak-in-go-http-standard-library Попробуйте 'go prof' – RoninDev

+0

Хм, это интересно, спасибо за ссылку, но я не Думаю, что да. Это внезапное и драматическое увеличение (начиная с момента запуска в 6 МБ до 400 МБ, отправляя эти 500 запросов против него). Я чувствую, что что-то мне не хватает. – Marc

+0

Смущенный тем, что вы пытаетесь сделать здесь. Конечно, 'defer resp.Body.Close()' не имеет никакого эффекта, вы делаете его 'ioutil.NopCloser'. Прочтите его, закройте его, прежде чем делать 'Close()' a no-op, а затем установите его, что происходит? – user3591723

ответ

3

Кузов должен быть закрыт после использования. См. this

Обратите внимание, что defer resp.Body.Close() вызывается после переназначения resp.Body новым значением.

2

ioutil.NopCloser делает Close() ничего не делать. Вы никогда не закрываете его, если отложите закрытие. Закрыть сразу после чтения.

bodyBytes, err := ioutil.ReadAll(resp.Body) 
if err != nil { 
    // err 
} 
resp.Body.Close() 
cachedBody := string(bodyBytes) 
// Close() does nothing after this 
resp.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) 

Кроме того, в Go вы обычно в состоянии игнорировать это else, потому что вы должны, вероятно, будет возвращаться сообщение об ошибке или обращении с ним в if над ним.

Такие моменты, когда было бы неплохо иметь sync.Pool, так как вы продолжаете утилизировать огромные буферы, проверьте, как он используется в пакете net/http для примера.

+0

Я выделил все, что связано с gist, связанное в моем редактировании. Я звоню близко, но все еще вижу, что память прыгает с ~ 1 мб до 400 мб всего за пару секунд. Если у вас есть время, сообщите мне, если вы увидите тот же результат на вашей машине. Благодаря! – Marc

+0

Если ваша ошибка не 'nil', вы не закрываете. Вы уверены, что ваша ошибка 'nil'? Вы также ничего не делаете с первой ошибкой. Вы должны что-то делать с ошибками. – user3591723

+0

Да, я забыл обработать ошибку, но она не срабатывает. Я просто перезапустил свой компьютер с заявлением на печать, чтобы проверить его. – Marc

Смежные вопросы