Go网络超时需区分连接建立与读写操作:连接超时用net.Dialer.Timeout,读写超时用SetReadDeadline/SetWriteDeadline;HTTP请求优先配置http.Client.Timeout及Transport各阶段超时。
在 Go 中处理网络超时,关键在于区分“连接建立超时”和“读写操作超时”,两者需用不同机制控制:前者用 DialTimeout(或更推荐的 net.Dialer.Timeout),后者用 SetDeadline、SetReadDeadline 或 SetWriteDeadline。
直接调用 net.DialTimeout 虽然可用,但已标记为 deprecated(自 Go 1.17 起)。现代写法应使用 net.Dialer 并设置 Timeout 字段:
Timeout 控制从开始拨号到 TCP 连接成功(三次握手完成)的最大耗时KeepAlive 可选,用于启用 TCP keep-alive 探测Resolver 设置 PreferGo 和自定义 Timeout
示例:
dialer := &net.Dialer{
Timeout: 5 * time.Second,
KeepAlive: 30 * time.Second,
}
conn, err := dialer.Dial("tcp", "example.com:80")
if err != nil {
// 连接失败:可能是超时、拒绝连接、DNS 错误等
log.Fatal(err)
}
SetDeadline 同时影响读和写,而 SetReadDeadline 和 SetWriteDeadline 可分别设置。注意:每次读/写前都需重新设置,因为 de
adline 是“绝对时间点”,且一旦触发超时,连接不会自动关闭,需手动处理。
time.Time 类型,不是持续时间;常用 time.Now().Add(...) 计算Read 或 Write 返回 os.ErrDeadlineExceeded(可类型断言判断)http.Client.Timeout,它内部已封装了连接、响应头、body 读取的多级超时示例(TCP 客户端读取响应):
conn.SetReadDeadline(time.Now().Add(3 * time.Second))
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
log.Println("read timeout")
}
return
}
对于 HTTP 请求,不建议在底层连接上手动调用 SetDeadline。标准库 http.Client 提供了更合理、分层的超时控制:
Timeout:整个请求生命周期(含连接、写请求、读响应头、读响应体)Transport 中可单独设 IdleConnTimeout、TLSHandshakeTimeout、ResponseHeaderTimeout 等ResponseHeaderTimeout + 自定义 Body 读取逻辑(配合 io.LimitReader 或 context)示例:
client := &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
TLSHandshakeTimeout: 5 * time.Second,
ResponseHeaderTimeout: 3 * time.Second,
},
}
resp, err := client.Get("https://api.example.com/data")
容易混淆的点:
DialTimeout 不等于 SetDeadline:前者只管建连,后者管数据收发SetDeadline 和 context,可能造成逻辑冲突;建议统一用 context 驱动(如 http.NewRequestWithContext)ReadFromUDP 前设置 ReadDeadline
实际项目中,优先组合使用 net.Dialer + http.Client 配置,复杂协议才手动管理 deadline。超时值应根据服务 SLA、网络环境和重试策略综合设定,避免过短引发频繁失败,过长拖慢整体响应。