记一次net http内存泄漏
注:本文已发布超过一年,请注意您所使用工具的相关版本是否适用
使用 gin 作为文件下载服务器,内存占用突然从几十 M 到了 10G 以上,导致服务被 kill 重启
复现
server.go
1 |
|
client.go
1 |
|
使用pprof
我们可以发现内存占用高达3GB
, 即使我主动调用了 GC 这个内存仍未释放
追溯
通过查看代码我们可以发现请求已经结束,代码并没有其他地方对[]byte
引用,一直追溯到最低层也不见其他引用。
但是结束client
进程之后会有一个神奇的发现,结束 client 之后这一块内存就可以被 GC 掉
通过这个现象自然而然的就想到可能是 TCP 链接没有断开,导致这一块内存的引用并没有被释放掉
http 是一个本身是短连接,但是为了复用 TCP 连接所以有了keep-alive
,但是对于下载服务来说我们其实不用复用 TCP 连接,只需要在文件下载完毕之后主动关闭这个连接即可,所以我分别在 client 加上了一个 header
1 |
|
再次通过pprof
查看内存占用发现内存仍未得到释放
原因
通过 rfc 文档,我们可以发现规范并没有规定由谁来关闭链接,Go net/http 希望客户端关闭链接
https://tools.ietf.org/html/rfc2616#page-117
HTTP/1.1 defines the “close” connection option for the sender to signal that the connection will be closed after completion of the response.
解决
- 使用流而不是直接读内存,在
gin
中不要直接使用c.Data
而是使用c.DataFromReader
- 使用 AWS S3 等存储服务下发文件,减轻服务压力
- 尽量不使用官方的
net/http
处理文件 - @jiajun 老师 已经给 Go 官方提了一个 PR,等待 PR Merge
参考资料
- https://jiajunhuang.com/articles/2018_11_24-memory_leak_in_net_http.md.html
- https://tools.ietf.org/html/rfc2616
- https://lailin.xyz/post/notes/pprof-go%E6%80%A7%E8%83%BD%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7/
关注我获取更新
猜你喜欢
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议,转载请注明出处,禁止全文转载