Go的http.Client默认复用连接但常失效,根本原因是连接池被干扰:未调用resp.Body.Close()、每次新建Client实例、服务端返回Connection: close、host不一致或Transport未配置MaxIdleConnsPerHost等关键参数。
Go 的默认就复用连接,不需要手动开 Keep-Alive,但默认配置在生产环境大概率失效。
为什么明明没关 Keep-Alive,连接还是不复用
现象是每次请求都新建 TCP 连接(
看到大量
或
),根本原因不是 Go “没启用”,而是连接池被干扰或未生效:
忘记调用
:连接卡在“使用中”,无法归还池子,这是最高频原因
用了
或每次
新建实例:Transport 未共享,连接池各自为政
服务端返回了
(比如 Nginx 对 4xx/5xx 默认加该头)
请求 URL host 不一致(
和
被视为不同 host)
自定义
时没设
,沿用默认值 100,高并发下池子快速耗尽
必须改的三个
参数
才是连接池本体,Client 只是壳。不配这三项,等于没调优:
:全局最大空闲连接数,建议
~
;若只调一个后端,设
(不限制)也常见
:单 host 最大空闲连接数,比上面更关键;一般设
,QPS 高可提至
;注意它不能超过
,否则会被静默截断
:空闲连接存活时间,推荐
;需略小于服务端 keepalive timeout(如 Nginx 默认
),避免被对方先断
别漏掉
和
,否则 DNS 卡住或服务端不写 header 会导致 goroutine 泄漏。
go语言参考手册 中文CHM版
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译
下载
不是习惯,是硬性要求
哪怕你只读
,也必须关闭 body,否则连接永远无法复用:
错误写法:
后没
看似安全的
在 panic 或提前
时可能不执行
更稳妥的是读完立刻关:
,或包一层
如果只读 header,用
再关,防止缓冲区未消费导致连接滞留
Client 实例和 Transport 怎么共享才安全
必须共享,且方式有明确约束:
是线程安全的,应作为包级变量或依赖注入单例使用,
禁止在 handler 或循环里 new Client
若需不同超时策略(如一个接口 5s,另一个 30s),应新建
,再传给新
;不要新建
却复用旧
多个
共享同一个
是安全的,也是推荐做法;但
本身不可并发修改字段(如运行中改
)
别用
做业务请求,它底层
无法调整参数,且易与其他库冲突
真正容易被忽略的是:HTTP/2 场景下,
不再起作用,但
仍控制底层 TCP 连接生命周期;而服务端降级到 HTTP/1.1 且未正确处理
头时,复用会无声中断。
http.ClientnetstatESTABLISHEDTIME_WAITresp.Body.Close()http.DefaultClient&http.Client{}Connection: closeapi.example.comAPI.EXAMPLE.COMhttp.TransportMaxIdleConnsPerHosthttp.Transporthttp.TransportMaxIdleConns2005000MaxIdleConnsPerHost100200MaxIdleConnsIdleConnTimeout60 * time.Second75sTLSHandshakeTimeoutResponseHeaderTimeoutresp.Body.Close()resp.StatusCodedata, _ := io.ReadAll(resp.Body)Close()defer resp.Body.Close()return_ = resp.Body.Close()defer func() { _ = resp.Body.Close() }()io.Copy(io.Discard, resp.Body)http.Clienthttp.Transporthttp.ClientClientTransportClientTransportTransportMaxIdleConnshttp.DefaultClientTransportMaxIdleConnsPerHostIdleConnTimeoutConnection