Go 健壮 HTTP 客户端:超时、重试、连接复用与中间件式 RoundTrip
test12026-10-063 次阅读
为什么不能直接用默认 http.Get
net/http 的默认 Client 没有总超时,遇到后端挂起会一直占着 goroutine;默认也不复用连接、不做重试。生产环境的对外调用应当显式配置 Transport、超时与重试策略。
配置带超时与连接池的 Client
package main
import (
"net/http"
"time"
)
func newClient() *http.Client {
transport := &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
}
return &http.Client{
Transport: transport,
Timeout: 5 * time.Second, // 整个请求的上限
}
}
用自定义 RoundTripper 实现重试
RoundTrip 是每次请求的必经之处。包一层即可在失败时按次数重试,并用 http.Request.Clone 重建请求体。
type retryer struct {
next http.RoundTripper
maxTries int
}
func (r *retryer) RoundTrip(req *http.Request) (*http.Response, error) {
var resp *http.Response
var err error
for i := 0; i < r.maxTries; i++ {
resp, err = r.next.RoundTrip(req.Clone(req.Context()))
if err == nil && resp.StatusCode < 500 {
return resp, nil
}
if resp != nil {
resp.Body.Close()
}
}
return resp, err
}
把重试接入 Client
client := &http.Client{
Transport: &retryer{next: transport, maxTries: 3},
Timeout: 5 * time.Second,
}
要点
- 超时永远要设:连接、读写、总超时分别用 DialContext、ResponseHeaderTimeout 与 Client.Timeout 控制。
- 重试只对幂等请求(GET、PUT)做,POST 等写操作需业务层确认。
- 5xx 与网络错误才重试,4xx 通常是参数问题,重试无意义。
- Transport 复用同一个实例,连接池才生效;不要每次请求 new 一个 Transport。
T
test1
文章作者
为什么不能直接用默认 http.Get net/http 的默认 Client 没有总超时,遇到后端挂起会一直占着 g...
- 分类
- 技术
- 发布时间
- 2026-10-06
- 字数
- 约 1256 字
- 阅读
- 3 次
