HTTP客户端超时必须显式设置,DefaultClient无默认超时;总超时用Client.Timeout,分阶段控制需自定义Transport各字段;服务端须设Read/WriteTimeout或更合理的IdleTimeout。

HTTP客户端请求超时必须显式设置
Go 的 http.DefaultClient 默认不设超时,一旦后端卡住或网络中断,http.Get() 或 client.Do() 可能无限阻塞。这不是 bug,是设计选择——但生产环境绝不能依赖默认行为。
- 超时需在
http.Client实例上通过Timeout、Transport的各阶段字段分别控制 -
Timeout是“总超时”,从发起请求到收到完整响应体结束,它会覆盖底层 Transport 的设置 - 若需更精细控制(比如只限制连接建立、读响应头、读响应体),必须自定义
http.Transport
用 http.Client.Timeout 快速启用总超时
最简方式:构造带 Timeout 的客户端。适用于大多数对端可控、不需要区分连接/读取阶段的场景。
client := &http.Client{
Timeout: 10 * time.Second,
}
resp, err := client.Get("https://api.example.com/data")
if err != nil {
// 可能是 net/http: request canceled (Client.Timeout exceeded)
log.Println("request failed:", err)
return
}- 错误类型通常是
net/http: request canceled (Client.Timeout exceeded),注意字符串匹配要完整 - 这个
Timeout会同时作用于 DNS 解析、TCP 连接、TLS 握手、发送请求、读取响应头和响应体 - 不推荐在高并发服务中复用一个设置了短
Timeout的全局http.Client,可能误杀长尾请求
用 http.Transport 分阶段控制超时
当需要区分“连不上”和“连上了但处理慢”,就得拆解 Transport 的超时参数。这是微服务间调用、网关代理等场景的常见需求。
transport := &http.Transport{
DialContext: (&net.Dialer{
Timeout: 5 * time.Second, // TCP 连接超时
KeepAlive: 30 * time.Second,
}).DialContext,
TLSHandshakeTimeout: 5 * time.Second, // TLS 握手超时
ExpectContinueTimeout: 1 * time.Second, // expect: 100-continue 等待超时
ResponseHeaderTimeout: 3 * time.Second, // 从发送完请求到收到响应头的超时
IdleConnTimeout: 30 * time.Second, // 空闲连接保活时间
MaxIdleConns: 100,
MaxIdleConnsPerHost: 100,
}
client := &http.Client{Transport: transport}-
ResponseHeaderTimeout是最关键的分界点:它不包含响应体读取,适合判断服务是否“已启动并响应” -
ReadTimeout已被弃用,不要用;响应体读取超时靠ResponseHeaderTimeout+ 手动io.CopyN或http.MaxBytesReader控制 - 所有超时值都建议设为不同,避免某阶段失败后掩盖真实瓶颈
服务器端读写超时容易被忽略
很多人只关注客户端超时,但 http.Server 同样需要设 ReadTimeout 和 WriteTimeout,否则恶意客户端可轻易耗尽连接数。
立即学习“go语言免费学习笔记(深入)”;
server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 5 * time.Second, // 读请求头+请求体的总时间
WriteTimeout: 10 * time.Second, // 写响应的总时间
IdleTimeout: 30 * time.Second, // 保持空闲连接的时间(Go 1.8+ 推荐用这个替代 ReadTimeout)
}-
IdleTimeout比ReadTimeout更合理:它只管连接空闲期,不干扰大文件上传等长耗时请求 -
ReadTimeout会强制中断正在读请求体的连接,对 POST 大数据很不友好 - 如果用了反向代理(如 Nginx),还要同步调整其
proxy_read_timeout等参数,否则 Go 服务提前断连会导致 502










