如何在Golang中处理网络超时_Golang net/http Timeout处理方法

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

HTTP客户端请求超时必须显式设置

Go 的 http.DefaultClient 默认不设超时,一旦后端卡住或网络中断,http.Get()client.Do() 可能无限阻塞。这不是 bug,是设计选择——但生产环境绝不能依赖默认行为。

  • 超时需在 http.Client 实例上通过 TimeoutTransport 的各阶段字段分别控制
  • 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.CopyNhttp.MaxBytesReader 控制
  • 所有超时值都建议设为不同,避免某阶段失败后掩盖真实瓶颈

服务器端读写超时容易被忽略

很多人只关注客户端超时,但 http.Server 同样需要设 ReadTimeoutWriteTimeout,否则恶意客户端可轻易耗尽连接数。

server := &http.Server{
    Addr:         ":8080",
    Handler:      mux,
    ReadTimeout:  5 * time.Second,   // 读请求头+请求体的总时间
    WriteTimeout: 10 * time.Second,  // 写响应的总时间
    IdleTimeout:  30 * time.Second,  // 保持空闲连接的时间(Go 1.8+ 推荐用这个替代 ReadTimeout)
}
  • IdleTimeoutReadTimeout 更合理:它只管连接空闲期,不干扰大文件上传等长耗时请求
  • ReadTimeout 会强制中断正在读请求体的连接,对 POST 大数据很不友好
  • 如果用了反向代理(如 Nginx),还要同步调整其 proxy_read_timeout 等参数,否则 Go 服务提前断连会导致 502