go的http.transport默认启用keep-alive,但需客户端和服务端协同支持:双方均需开启、响应头匹配、body正确关闭,且参数配置合理,否则连接无法复用。

Go 的 http.Transport 默认就开 Keep-Alive,但得配对才生效
Keep-Alive 不是单边开关,它需要客户端和服务端同时支持、协商成功。Go 的 http.DefaultTransport 默认启用了连接复用(DisableKeepAlives: false),但如果你自定义了 http.Transport 却没显式设回,就可能关掉了。
-
http.Transport的DisableKeepAlives默认是false,别手动设成true - 服务端也要响应
Connection: keep-alive(Go 的http.Server默认也开,除非你显式关了Server.Close()或设了Server.SetKeepAlivesEnabled(false)) - HTTP/2 会自动绕过 Keep-Alive 协商,但 HTTP/1.1 下,如果服务端返回
Connection: close,Go 客户端会立刻断开,哪怕 Transport 配置正确
连接复用失效的典型现象:每请求都新建 TCP 连接
用 netstat -an | grep :80 或 ss -tan | grep :80 观察客户端端口变化——如果每次请求都出现新端口号,说明连接没复用;Wireshark 抓包看到连续的 SYN → SYN-ACK → ACK 就是铁证。
- 常见诱因:服务端返回了
Connection: close响应头(比如 Nginx 默认在 4xx/5xx 时加这个头) - 客户端用了短生命周期的
http.Client(比如函数内 new 一个 client 就扔掉),导致Transport没机会复用连接 - 设置了过小的
MaxIdleConnsPerHost(默认是 2),并发请求数超了,多余请求只能等或新建连接 - 请求头里手动写了
Connection: close,Go 不会覆盖它
MaxIdleConns 和 MaxIdleConnsPerHost 别乱调
这两个参数控制空闲连接池大小,不是越大越好。设太高会占内存、拖慢 GC;设太低则频繁建连,抵消 Keep-Alive 优势。
-
MaxIdleConns是全局总数上限,MaxIdleConnsPerHost是每个 host(如 api.example.com:443)的上限,后者必须 ≤ 前者,否则会被静默截断 - 默认值保守(
MaxIdleConnsPerHost=2),高并发场景建议设为 20~100,视后端承载能力而定 - 如果后端是单实例且连接数有限(比如 MySQL Proxy 限制 1000 连接),客户端总空闲连接数别超过它的 1/3
- 调完记得观察
http.Transport.IdleConnStats,看IdleConnTimeout是否频繁触发关闭(说明连接空太久被回收,复用率低)
HTTP/1.1 下 Keep-Alive 超时由双方共同决定
Go 客户端用 IdleConnTimeout(默认 30s),服务端用自己的 keepalive timeout(如 Nginx 的 keepalive_timeout)。实际生效的是更短的那个。
立即学习“go语言免费学习笔记(深入)”;
- 若服务端设了 5s,客户端设了 30s,那连接空闲 5s 后服务端就发 FIN,客户端下次复用时会报
read: connection reset by peer或i/o timeout - 建议客户端
IdleConnTimeout设为服务端值的 0.8 倍,留点余量 -
Response.Body必须被读完或显式io.Copy(ioutil.Discard, resp.Body); resp.Body.Close(),否则连接无法归还到 idle 池 - 不要用
defer resp.Body.Close()在长循环里——如果中间 panic,defer 可能不执行,连接泄漏










