跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

Go 语言中 net/http 连接复用的 Keep-Alive 机制

Go的http.Client默认复用连接但常失效,根本原因是连接池被干扰:未调用resp.Body.Close()、每次新建Client实例、服务端返回Connection: close、host不一致或Transport未配置MaxIdleConnsPerHost等关键参数。 Go 的
http.Client
默认就复用连接,不需要手动开 Keep-Alive,但默认配置在生产环境大概率失效。 为什么明明没关 Keep-Alive,连接还是不复用 现象是每次请求都新建 TCP 连接(
netstat
看到大量
ESTABLISHED
或
TIME_WAIT
),根本原因不是 Go “没启用”,而是连接池被干扰或未生效: 忘记调用
resp.Body.Close()
:连接卡在“使用中”,无法归还池子,这是最高频原因 用了
http.DefaultClient
或每次
&http.Client{}
新建实例:Transport 未共享,连接池各自为政 服务端返回了
Connection: close
(比如 Nginx 对 4xx/5xx 默认加该头) 请求 URL host 不一致(
api.example.com
和
API.EXAMPLE.COM
被视为不同 host) 自定义
http.Transport
时没设
MaxIdleConnsPerHost
,沿用默认值 100,高并发下池子快速耗尽 必须改的三个
http.Transport
参数
http.Transport
才是连接池本体,Client 只是壳。不配这三项,等于没调优:
MaxIdleConns
:全局最大空闲连接数,建议
200
~
500
;若只调一个后端,设
0
(不限制)也常见
MaxIdleConnsPerHost
:单 host 最大空闲连接数,比上面更关键;一般设
100
,QPS 高可提至
200
;注意它不能超过
MaxIdleConns
,否则会被静默截断
IdleConnTimeout
:空闲连接存活时间,推荐
60 * time.Second
;需略小于服务端 keepalive timeout(如 Nginx 默认
75s
),避免被对方先断 别漏掉
TLSHandshakeTimeout
和
ResponseHeaderTimeout
,否则 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数组安全、编译 下载
resp.Body.Close()
不是习惯,是硬性要求 哪怕你只读
resp.StatusCode
,也必须关闭 body,否则连接永远无法复用: 错误写法:
data, _ := io.ReadAll(resp.Body)
后没
Close()
看似安全的
defer resp.Body.Close()
在 panic 或提前
return
时可能不执行 更稳妥的是读完立刻关:
_ = resp.Body.Close()
,或包一层
defer func() { _ = resp.Body.Close() }()
如果只读 header,用
io.Copy(io.Discard, resp.Body)
再关,防止缓冲区未消费导致连接滞留 Client 实例和 Transport 怎么共享才安全 必须共享,且方式有明确约束:
http.Client
是线程安全的,应作为包级变量或依赖注入单例使用, 禁止在 handler 或循环里 new Client 若需不同超时策略(如一个接口 5s,另一个 30s),应新建
http.Transport
,再传给新
http.Client
;不要新建
Client
却复用旧
Transport
多个
Client
共享同一个
Transport
是安全的,也是推荐做法;但
Transport
本身不可并发修改字段(如运行中改
MaxIdleConns
) 别用
http.DefaultClient
做业务请求,它底层
Transport
无法调整参数,且易与其他库冲突 真正容易被忽略的是:HTTP/2 场景下,
MaxIdleConnsPerHost
不再起作用,但
IdleConnTimeout
仍控制底层 TCP 连接生命周期;而服务端降级到 HTTP/1.1 且未正确处理
Connection
头时,复用会无声中断。

相关文章