必须用http.roundtripper,因它能捕获dns解析、tls握手、连接复用等待、响应体流式读取等全链路耗时;需在roundtrip入口打点、包装resp.body并在close中补全终点时间。

Go 中记录外部 API 调用耗时必须用 http.RoundTripper
标准库里唯一能捕获 DNS 解析、连接建立、TLS 握手、响应体读取等全链路耗时的位置,就是自定义 http.RoundTripper。你在业务层调用 http.Client.Do() 或封装一个 api.Call() 函数打点,漏掉的耗时可能高达几百毫秒——尤其是首次请求、高延迟网络或流式接口(如 SSE)场景。
常见错误包括:
- 在
Do()前后调用time.Now():只覆盖「发起请求」到「拿到*http.Response」之间,不包含resp.Body.Read()和Close()阶段 - 把计时逻辑塞进中间件(比如 Gin 的
Use()):那只是服务端内部处理耗时,和外部调用无关 - 直接改写
http.DefaultTransport字段:它是全局可变变量,高并发下会引发竞态
怎么包装 RoundTrip 并劫持 resp.Body
核心是两件事:入口打点 + 出口补点。入口在 RoundTrip 开始时记 start := time.Now();出口不能靠 RoundTrip 返回就结束,必须包装 resp.Body,在它的 Close() 方法里才算真正完成。
实操要点:
-
RoundTrip内部先调t.base.RoundTrip(req),拿到resp后立刻包装resp.Body,不要做任何阻塞操作 - 自定义
timingReadCloser类型需嵌入原io.ReadCloser,重写Close(),并在其中调用time.Since(start) - 别在
Close()里做日志打印或网络上报——用 channel 异步投递,避免阻塞后续读取 - 如果请求失败(
err != nil),也要在RoundTrip内记录耗时,否则错误请求永远不进监控
示例关键片段:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
type timingReadCloser struct {
io.ReadCloser
req *http.Request
start time.Time
}
<p>func (t *timingReadCloser) Close() error {
duration := time.Since(t.start).Milliseconds()
// 投递到 statsChan,不阻塞
statsChan </p><h3>为什么必须包装 <code>resp.Body</code> 而不只是看 <code>RoundTrip</code> 返回时间</h3><p>HTTP 响应体是流式读取的。<code>RoundTrip</code> 返回时,TCP 连接可能还在传输数据,<code>resp.Body.Read()</code> 可能阻塞数秒,尤其在大文件下载、JSON 流、SSE 场景下。不包装 <code>Body</code>,你统计的只是「发出去又收到 header」的时间,不是真实端到端耗时。</p><p>容易被忽略的细节:</p>
- 同一个
resp.Body可能被多次Read(),但只有Close()是确定的终点 - 若业务代码忘了调用
resp.Body.Close(),耗时永远不会上报——需配合context.WithTimeout或defer resp.Body.Close()确保执行 - 包装后的
timingReadCloser必须透传所有Read()调用,不能拦截或修改内容,否则破坏业务逻辑
如何避免日志/指标写入拖慢请求
高频外部调用下,同步写文件、发 HTTP 上报、甚至调用 log.Printf 都可能成为瓶颈。最稳的做法是异步聚合:
- 用带缓冲的
chan StatsLog接收每次调用结果,缓冲大小建议设为 1024~4096 - 起一个单独 goroutine 消费 channel,批量写入本地文件、Prometheus
Counter或 Kafka - 结构体字段全用基础类型(
string,int64),避免指针或 interface{},减少 GC 压力 - 别在
Close()里调用json.Marshal或格式化字符串——提前序列化好再投递
真正难的不是怎么记,而是怎么让记耗时这件事本身不变成性能瓶颈。很多团队卡在这一步:指标越全,延迟越高,最后只能关掉。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










