【问题标题】:http.Response.Body copying leads to large memory usage increasehttp.Response.Body 复制导致大量内存使用增加
【发布时间】:2015-09-08 05:47:33
【问题描述】:

所以我试图从 http.Response 中获取响应主体,进行一些操作,然后将其设置回来。这是我第一次尝试在不耗尽 http.Response 的情况下将其取出:

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),服务器内存使用量会上升到 ~400mb,并且不会回来向下。这是正常的吗?上面的代码有问题吗?我是不是漏掉了一些东西来释放内存?

defer resp.Body.Close() 无效。

谢谢!


编辑 1

这里有一个简单版本的代理的要点,包括建议的近距离通话。如果位于 localhost:3000(它的代理地址)的服务器返回较大的响应,则内存使用量将迅速增加。就我而言,我返回一个 1mb 的 json 文件,通过 go 代理发送 500 个请求会将内存使用量增加到 400mb 左右。

go 代理:
https://gist.github.com/marbemac/300c4c376171cbd27cc3

一个简单的节点服务器,它在同一目录中返回一个名为 large_response.json 的文件。
https://gist.github.com/marbemac/55aaa78f5858484d33f6

【问题讨论】:

  • 也许这可以帮助你stackoverflow.com/questions/21080642/…试试go prof
  • 嗯,这很有趣,感谢您的链接,但我不认为就是这样。这是一个突然而剧烈的增长(在几秒钟内从 6mb 开始状态到 400mb,发送了这 500 个请求)。我觉得我缺少一些东西。
  • 对您在这里尝试做的事情感到困惑。当然defer resp.Body.Close() 没有效果,你把它变成ioutil.NopCloser。阅读它,在使Close() 成为空操作之前关闭它,然后设置它,会发生什么?
  • 也许你需要在bodyBytes, err := ioutil.ReadAll(resp.Body)之后resp.Body.Close()
  • 我明白了。哪种类型的内存“上升”?很可能它根本不重要,因为这是虚拟内存。也许您可以通过go test -benchmem 和适当的基准来衡量分配,以查看您是否分配并执行真实的内存配置文件。 “内存”如 top 等人所示。上去不放出来这个没问题。

标签: go


【解决方案1】:

使用后必须关闭机身。参考this

请注意,在您使用新值重新分配 resp.Body 后会调用 defer resp.Body.Close()

【讨论】:

    【解决方案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 包中的使用方式作为示例。

    【讨论】:

    • 我将所有内容都与我编辑中链接的要点隔离开来。我打电话给关闭,但仍然看到内存在几秒钟内从 ~1mb 跃升至 400mb。如果您有时间,请告诉我您是否在机器上看到相同的结果。谢谢!
    • 如果您的错误不是nil,您就没有关闭。你确定你的错误是nil吗?你也没有对第一个err 做任何事情。你必须实际做一些错误的事情..
    • 是的,我忘了处理错误,但它没有被触发。我刚刚在我的电脑上重新运行了一个打印语句来检查。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-22
    • 2015-04-19
    • 2022-06-20
    • 1970-01-01
    • 2010-12-08
    • 1970-01-01
    相关资源
    最近更新 更多