net/http 服务端超时怎么控制?keep-alive 又是怎么回事?
一句话结论:http.Server 提供四类超时:ReadHeaderTimeout 防慢请求头(slowloris)、ReadTimeout 限制读完整个请求与 body、WriteTimeout 限制写响应、IdleTimeout 控制 keep-alive 空闲连接;业务处理自身的耗时则用 http.TimeoutHandler 或 context 超时兜底。keep-alive 默认开启(连接复用),IdleTimeout 不设则回落到 ReadTimeout。
字段与建议
| 字段 | 管什么 | 建议 |
|---|---|---|
| ReadHeaderTimeout | 从连接建立到读完请求头 | 必设,如 5s,防 slowloris |
| ReadTimeout | 读完整个请求(含 body) | 有上传就设,如 30s |
| WriteTimeout | 写完响应(防慢客户端) | 如 30s;注意约束长轮询类接口 |
| IdleTimeout | keep-alive 空闲连接存活 | 如 60s;为 0 时取 ReadTimeout 值 |
| MaxHeaderBytes | 请求头上限 | 默认 1MB,可按需调小 |
HTTP/2 下 WriteTimeout 的语义与 HTTP/1.1 不同,别指望它精确限制单个流;应用层业务超时统一交给 TimeoutHandler 或 context.WithTimeout 更可控。
keep-alive 补充
- 默认开启:同一 TCP 连接服务多个请求,省去反复握手,吞吐更高。
- 关闭:服务端 srv.SetKeepAlivesEnabled(false),或要求客户端发 Connection: close。
- 客户端侧:用 http.Client{Timeout: ...} 设总超时,Transport 的 DialContext / IdleConnTimeout / TLSHandshakeTimeout 管连接细节。
配置示例
srv := &http.Server{
Addr: ":8080",
Handler: http.TimeoutHandler(mux, 10*time.Second, "timeout"),
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 60 * time.Second,
}
log.Fatal(srv.ListenAndServe())
TimeoutHandler 到达时限后向客户端返回 503,并切断与 handler 的关联,防止 handler 无限挂起占资源。
常见追问 / 记忆点
- 追问:只设 ReadTimeout 不设 ReadHeaderTimeout 行吗?——行但不推荐:ReadTimeout 从连接建立起算,慢请求头攻击仍可能得手。
- 追问:keep-alive 关掉会怎样?——每次请求都重新建连,握手开销放大,QPS 明显下降。
- 记忆点:Header 超时防 slowloris;IdleTimeout 管 keep-alive;业务耗时交给 TimeoutHandler/context。