You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用io.ReadAll读取HTTP响应体时出现goroutine泄漏问题求助

解决HTTP请求后goroutine泄漏问题

你遇到的goroutine泄漏是因为Go标准库的http.Client默认启用了HTTP/2传输,HTTP/2连接会启动后台goroutine(比如http2ClientConn的readLoop)来持续读取连接上的帧数据。这些goroutine在连接处于空闲状态时不会立即退出,测试结束时goleak就会把它们判定为意外goroutine。

下面是三种可行的解决办法:

方法1:禁用HTTP/2,强制使用HTTP/1.1

通过修改http.Transport的TLSNextProto字段为空map,让客户端只使用HTTP/1.1,这样就不会启动HTTP/2相关的后台goroutine:

func TestLeak(t *testing.T) {
    defer goleak.VerifyNone(t)

    request, err := http.NewRequest(http.MethodGet, "https://google.com", nil)
    assert.NilError(t, err)

    // 自定义Transport禁用HTTP/2
    client := http.Client{
        Transport: &http.Transport{
            TLSNextProto: make(map[string]func(authority string, c *tls.Conn) http.RoundTripper),
        },
    }

    response, err := client.Do(request)
    assert.NilError(t, err)

    defer response.Body.Close()

    _, err = io.ReadAll(response.Body)
    assert.NilError(t, err)
}

方法2:显式关闭Transport的空闲连接

如果你需要保留HTTP/2,可以自己创建独立的http.Transport实例,在测试结束前调用CloseIdleConnections()主动关闭所有空闲连接,触发相关goroutine退出:

func TestLeak(t *testing.T) {
    defer goleak.VerifyNone(t)

    request, err := http.NewRequest(http.MethodGet, "https://google.com", nil)
    assert.NilError(t, err)

    // 创建独立Transport,测试结束关闭空闲连接
    transport := &http.Transport{}
    defer transport.CloseIdleConnections()

    client := http.Client{Transport: transport}

    response, err := client.Do(request)
    assert.NilError(t, err)

    defer response.Body.Close()

    _, err = io.ReadAll(response.Body)
    assert.NilError(t, err)
}

方法3:让goleak忽略HTTP/2相关goroutine

如果必须保留HTTP/2且不想主动关闭连接,可以配置goleak忽略那些HTTP/2后台goroutine的栈顶函数:

func TestLeak(t *testing.T) {
    // 忽略http2ClientConn的readLoop goroutine
    defer goleak.VerifyNone(t, goleak.IgnoreTopFunction("net/http.(*http2ClientConn).readLoop"))

    request, err := http.NewRequest(http.MethodGet, "https://google.com", nil)
    assert.NilError(t, err)

    client := http.Client{}

    response, err := client.Do(request)
    assert.NilError(t, err)

    defer response.Body.Close()

    _, err = io.ReadAll(response.Body)
    assert.NilError(t, err)
}

各方法适用场景

  • 方法1:适合不需要HTTP/2特性的场景,简单直接。
  • 方法2:适合需要HTTP/2且希望严格清理资源的测试场景。
  • 方法3:适合必须保留HTTP/2连接复用的场景,仅在测试中忽略预期的goroutine。

内容的提问来源于stack exchange,提问作者wxh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 00:40:18