go程序内存打满之goroutine泄露溢出之http的response.Body 未关闭

一段程序跑了一天一夜,内存打爆了。排查代码自己写的创建 goroutine 代码无持续性,且有退出机制。

使用

使用 pprof 工具排查

https://blog.csdn.net/kenkao/article/details/96300389

package main

import (
    "bytes"
    "fmt"
    "net/http"
    _ "net/http/pprof" //查看内存占用使用
    "net/url"
    "time"
)

func main() {//测试不关闭response.body
    //_ "net/http/pprof" //查看内存占用使用 访问http://127.0.0.1:12345/debug/pprof/查看内存申请和    goroutine 创建数量
    // pprof 的init函数会将pprof里的一些handler注册到http.DefaultServeMux上
    // 当不使用http.DefaultServeMux来提供http api时,可以查阅其init函数,自己注册handler

    go func() {
        fmt.Println("http.ListenAndServe(\"0.0.0.0:12348\" befor, nil)")
        http.ListenAndServe("0.0.0.0:12348", nil)
        fmt.Println("http.ListenAndServe(\"0.0.0.0:12348\" after, nil)")
    }()
    for {
        addr := "https://www.baidu.com"
        urlObj, _ := url.Parse(addr)
        fmt.Println(urlObj.String())
        request, err := http.NewRequest("GET", urlObj.String(), bytes.NewBuffer([]byte{}))
        fmt.Println("request.Body:", request.Body)
        defer request.Body.Close()
        if err != nil {
            fmt.Println("http.NewRequest:", err)
        }
        r, err := http.DefaultClient.Do(request)
        //***********核心代码,如果不关闭Body,则产生的goroutine不释放*********************
        // 解决方式一、r.Body.Close()
        if r.Body != nil {
            r.Body.Close()
        }
        // 解决方式二、将.Body内容全部读取,由io.ReadAll()实现关闭
        //recvBytes, err := io.ReadAll(r.Body)
        //bodyStr := string(recvBytes)
        //fmt.Println(bodyStr)
        fmt.Println("r.status:", r.StatusCode, r.Status)
        time.Sleep(time.Second)
    }
}

response.Body不关闭时,查看goroutine 数量每次请求都会+2

 关闭后保持稳定状态

另有发现老文章:https://zhuanlan.zhihu.com/p/391917096

posted on 2023-02-26 23:00  zhangmingda  阅读(102)  评论(0编辑  收藏  举报

导航