u1timate
发布于 2021-10-11 / 2,161 阅读
0
0

golang HTTP 连接未被释放的问题

采用短连接的方式频繁请求某个URL,使用如下代码

client := http.Client{
	Transport: &http.Transport{
		DialContext: (&net.Dialer{
			Timeout: timeout,
		}).DialContext,,
	},
}
resp, err := client.Post(URL, "application/json", nil)
if err != nil {
	return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)

如果对端不先关闭该连接(对端发FIN包)的话,我们这边即便是调用了resp.Body.Close()函数仍然不会改变这些处于ESTABLISHED状态的连接。

Golang的net包中client.go, transport.go, response.go和request.go这几个文件中实现了HTTP Client。当应用层调用client.Do()函数后,transport层会首先找与该请求相关的已经缓存的连接(这个缓存是一个map,map的key是请求方法、请求地址和proxy地址,value是一个叫persistConn的连接描述结构),如果已经有可以复用的旧连接,就会在这个旧连接上发送和接受该HTTP请求,否则会新建一个TCP连接,然后在这个连接上读写数据。当client接受到整个响应后,如果应用层没有调用response.Body.Close()函数,刚刚传输数据的persistConn就不会被加入到连接缓存中,这样如果您在下次发起HTTP请求的时候,就会重新建立TCP连接,重新分配persistConn结构,这是不调用response.Body.Close()的一个副作用。
      如果不调用response.Body.Close()还存在一个问题。如果请求完成后,对端关闭了连接(对端的HTTP服务器向我发送了FIN),如果这边不调用response.Body.Close(),那么可以看到与这个请求相关的TCP连接的状态一直处于CLOSE_WAIT状态(还记得么?CLOSE_WAIT是连接的半开半闭状态,它是收到对方的FIN并且我们也发送了ACK,但是本端还没有发送FIN到对端,如果本段不调用close关闭连接,那么连接将一直处于CLOSE_WAIT状态,不会被系统回收)。
调用了response.Body.Close()就万无一失了么?上面代码中也调用了body.Close()为什么还会有很多ESTABLISHED状态的连接呢?因为在函数DoRequest()的每次调用中,我们都会新创建transport和client结构,当HTTP请求完成并且接收到响应后,如果对端的HTTP服务器没有关闭连接,那么这个连接会一直处于ESTABLISHED状态。如何解呢?
第一个方法是用一个全局的client,函数DoRequest()中每次都只在这个全局client上发送数据。但是如果我就想用短连接呢?用方法二。
第二个方法是在transport分配时将它的DisableKeepAlives参数置为false,像下面这样:

transport := http.Transport{
    Dial:              dialTimeout,
    DisableKeepAlives: true,
        }

2 https连接关闭

当使用golang http.client尝试用https协议访问目标网站的时候,如果目标网站不是https网站,则有可能出现函数response结果后tcp连接依然establish的情况。
56C6D25FFA0543E9BA85B3D0962F0E64.png
这时候需要设置TLS握手的超时时间,即可解决该问题。如下:

c = http.Client{
	Timeout: timeout,
	Transport: &http.Transport{
		TLSClientConfig:   &tls.Config{InsecureSkipVerify: true},
		DisableKeepAlives: true, //不使用keep-alive
		Proxy:             proxy,
		//解决非https网站尝试https连接无法释放连接的问题
		TLSHandshakeTimeout: timeout,
	},
}

参考

如何关闭Golang中的HTTP连接 How to Close Golang's HTTP connection - CobbLiu - 博客园 (cnblogs.com)


评论