Why is reading response body after calling http.Get(..) causing resource leaks even though I am closing the response body?

huangapple go评论84阅读模式
英文:

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")
}

huangapple
  • 本文由 发表于 2023年4月2日 02:26:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/75908115.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定