英文:
Why is reading response body after calling http.Get(..) causing resource leaks even though I am closing the response body?
问题
func getRespBody(link string) (string, error) {
res, err := http.Get(link)
if err != nil {
return "", err
}
defer res.Body.Close()
resBody, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", err
}
return string(resBody), nil
}
我有一个测试代码:
func TestRespBody(t *testing.T) {
defer goleak.VerifyNone(t)
getRespBody("https://google.com")
}
为什么这会导致资源泄漏?
我原本希望defer res.Body.Close()
会关闭响应体,从而避免资源泄漏。在这里应该怎么做才能防止资源泄漏?
英文:
func getRespBody(link string) (string, error) {
res, err := http.Get(link)
if err != nil {
return "", err
}
defer res.Body.Close()
resBody, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", err
}
return string(resBody), nil
}
and I have a test -
func TestRespBody(t *testing.T) {
defer goleak.VerifyNone(t)
getRespBody("https://google.com")
}
Why does this cause resource leaks?
I was hoping that defer res.Body.Close()
would take care of closing the resp body and there wouldn't be resource leak(s). What should be done in here to prevent resource leaks?
答案1
得分: 1
在关闭响应体时,默认情况下不会关闭HTTP调用中使用的底层TCP连接,因为重用它们是有优势的。这是正常的,但也是导致误报泄漏的原因。
如果将DisableKeepAlives设置为true,可以修改默认的HTTP客户端行为。
如果将函数重构为接收HTTP客户端作为参数,可以创建一个显式关闭连接而不是将其保存在连接池中的HTTP客户端,仅在该测试范围内使用。
func getResBody(c http.Client, url string) (string, error) {
res, err := c.Get(url)
if err != nil {
return "", err
}
defer res.Body.Close()
resBody, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", err
}
return string(resBody), nil
}
func Test_getResBody(t *testing.T) {
defer goleak.VerifyNone(t)
client := http.Client{
Transport: &http.Transport{
DisableKeepAlives: true,
},
}
getResBody(client, "https://google.com")
}
英文:
The underlying TCP connections used within the HTTP calls, are not closed by default when you close the response body, because it is advantageous to reuse them. This is normal, but that's also the cause of your false positive leak.
You can modify the default http client behavior if you set DisableKeepAlives true.
If you refactor your function to receive an HTTP client as an argument, you can craft an http client which explicitly closes the connection rather than saving it in the connection pool, just in the scope of that test.
func getResBody(c http.Client, url string) (string, error) {
res, err := c.Get(url)
if err != nil {
return "", err
}
defer res.Body.Close()
resBody, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", err
}
return string(resBody), nil
}
func Test_getResBody(t *testing.T) {
defer goleak.VerifyNone(t)
client := http.Client{
Transport: &http.Transport{
DisableKeepAlives: true,
},
}
getResBody(client, "https://google.com")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论