golang http请求 tcp连接没有关闭

发布时间 2023-11-30 10:25:51作者: bhxuwei

参考链接 https://studygolang.com/articles/3138

func dialTimeout(network, addr string) (net.Conn, error) {
	return net.DialTimeout(network, addr, time.Second*POST_REMOTE_TIMEOUT)
}

func DoRequest(URL string) xx, error {
       transport := http.Transport{
                Dial:              dialTimeout,
        }

        client := http.Client{
                Transport: &transport,
        }

        content := RequestContent{}
        // fill content here 

        postStr, err := json.Marshal(content)
        if err != nil {
                return nil, err
        }

        resp, err := client.Post(URL, "application/json", bytes.NewBuffer(postStr))
        if err != nil {
                return nil, err
        }

        defer resp.Body.Close()
        body, err := ioutil.ReadAll(resp.Body)
        if err != nil {
                return nil, err
        }

        // receive body, handle it
}

运行这段代码一段时间后会发现,该进程下面有一堆ESTABLISHED状态的连接(用lsof -p pid查看某进程下的所有fd),因为每次DoRequest函数被调用后,都会新建一个TCP连接,如果对端不先关闭该连接(对端发FIN包)的话,我们这边即便是调用了resp.Body.Close()函数仍然不会改变这些处于ESTABLISHED状态的连接。

在transport分配时将DisableKeepAlives参数置为true

req, err := http.NewRequest("POST", urlSso, nil)

	if err != nil {
		scikits.SugarLogger.Error(err)
		return userInfo
	}

	req.Header.Add("User-Agent", "apifox/1.0.0 (https://www.apifox.cn)")
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Cookie", "token="+token)

	// `这里请注意,使用 InsecureSkipVerify: true 来跳过证书验证`
	client := &http.Client{
		Timeout: 10 * time.Second,
		Transport: &http.Transport{
			// DisableKeepAlives 参数很关键,业务场景是服务A调用B,每次调用都会创建新的http client对象,这样每次请求都会创建一个连接,而我们的接口请求量很大,这样就会创建大量的连接,所以不能够启用连接池进行连接复用。
			DisableKeepAlives: true,
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		}}
	res, err := client.Do(req)
	if err != nil {
		scikits.SugarLogger.Error(err)
		return userInfo
	}

	defer res.Body.Close()

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return userInfo
	}

	//fmt.Println(string(body))
	type structGetCacheUserInfoTmp struct {
		Code int        `json:"code"`
		Data model.User `json:"data"`
	}

	var data structGetCacheUserInfoTmp
	json.Unmarshal(body, &data)
	userInfo = data.Data