go net http 笔记 连接复用
目前使用net/http 长连接 连接池复用时,出现连接一直都在关闭重连,
strace 后发现没有read respone 只读取了head,但是body没有读取,
google后看到官方问题,确实记录需要代码实现读取body逻辑来清空socket内核缓存,以便后面复用。
需要读取respone里面的body
io.Copy(ioutil.Discard, resp.Body)
也就是默认只是读取了header,但并未读取完response body数据。需要自己手动读取body,后面才可以复用这个链接。
没有执行 ioutil.ReadAll(resp.Body)
。也就是说http请求响应的结果并没有被读取的情况下,net/http库会怎么处理。
上面的代码最终输出3
,分别是main goroutine,read goroutine 以及write goroutine。也就是说长连接没有断开,那长连接是会在下一次http请求中被复用吗?先说答案,不会复用。
我们可以看代码。resp.Body.Close()
会执行到 func (es * bodyEOFSignal) Close() error
中,并执行到es.earlyCloseFn()
中。
earlyCloseFn
的逻辑也非常简单,就是将一个false传入到waitForBodyRead
的channel中。那写入通道后的数据会在另外一个地方被读取,我们来看下读取的地方。
bodyEOF
为false, 也就不需要执行 tryPutIdleConn()
方法。
tryPutIdleConn会将连接放到长连接池中备用)。
最终就是alive=bodyEOF
,也就是false
,字面意思就是该连接不再存活。因此该长连接并不会复用,而是会释放。
func (es *bodyEOFSignal) Close() error {
es.mu.Lock()
defer es.mu.Unlock()
if es.closed {
return nil
}
es.closed = true
//如果close body的时候,没有读取body 里面的数据,就会调用如下逻辑
//最后 会关闭内核层面socket
if es.earlyCloseFn != nil && es.rerr != io.EOF {
return es.earlyCloseFn()
}
err := es.body.Close()
return es.condfn(err)
}
同时http的连接复用是基于transport的。
比如get/post 请求的client使用相同的transport,就会链接复用
如下代码调用"http://127.0.0.1:8090/xxx 会链接复用
但是调用http://127.0.0.1:8080/xxx 不会链接复用,每次get都会创建一个tcp 连接
package main import ( "crypto/tls" "io" "log" "net/http" "time" ) func call_baidu() { tr := &http.Transport{ //TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, MaxIdleConnsPerHost: 0, MaxConnsPerHost: 0, MaxIdleConns: 0, } netClient := &http.Client{ Transport: tr, Timeout: time.Second * 120, } urlStr := "http://127.0.0.1:8080/xxx" resp, err := netClient.Get(urlStr) if err != nil { log.Printf("http get url:%s failed, err:%s", urlStr, err) return } defer resp.Body.Close() // 可以根据需要在这里处理响应,例如读取响应体 log.Printf("Successfully accessed %s, status code: %d", urlStr, resp.StatusCode) _, err = io.Copy(io.Discard, resp.Body) } func main() { ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() for range ticker.C { call_baidu() call_baidu() call_baidu() } } var tr1 *http.Transport = &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } func call_baidu1() { var netClient = &http.Client{Transport: tr1, Timeout: time.Second * 120} urlStr := "http://127.0.0.1:8090/xxx" resp, err := netClient.Get(urlStr) if err != nil { log.Printf("http get url:%s failed, err:%s", urlStr, err) return } defer resp.Body.Close() // 可以根据需要在这里处理响应,例如读取响应体 log.Printf("Successfully accessed %s, status code: %d", urlStr, resp.StatusCode) _, err = io.Copy(io.Discard, resp.Body) }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
2021-07-13 引擎限速-慢速攻击的思考