

新闻资讯
技术教程应使用 pprof + 自定义 Handler 统计真实响应时间,关注 goroutine 泄漏、HTTP 客户端超时配置、中间件与路由开销,避免未设超时的阻塞调用和 channel 泄漏。
http.Server 的实际响应时间分布Go 默认的 http.Server 不记录每条请求耗时,光靠日志里的 time.Now() 打点容易漏掉 TLS 握手、读取 body、写响应头等阶段。建议用 net/http/pprof + 自定义 Handler 组合观测:
pprof:在服务启动时注册 http.DefaultServeMux 下的 /debug/pprof/
WriteHeader 时间,而非仅 defer time.Since() —— 因为 WriteHeader 可能被延迟调用(比如 streaming 场景)http.Server.ReadTimeout 和 WriteTimeout 已被弃用,应改用 ReadHeaderTimeout、IdleTimeout 和 WriteTimeout(Go 1.19+)响应慢常因 goroutine 积压,而非 CPU 高。用 /debug/pprof/goroutine?debug=2 查看全量堆栈,重点关注:
select 或 chan receive 状态的 goroutine(说明 channel 未被消费或 sender 已死)database/sql.(*DB).QueryRow 或 http.(*Client).Do —— 很可能是连接池耗尽或下游无响应runtime.SetMutexProfileFraction(1) 后访问 /debug/pprof/mutex,确认是否有锁竞争热点
Web 服务常依赖下游 API,但默认 http.Client 没设超时,会导致请求 hang 死:
http.Client.Timeout 不控制 DNS 解析、TLS 握手、连接建立,需单独设 Transport 的 DialContext 和 TLSHandshakeTimeout
MaxIdleConnsPerHost 的全局 http.Client,否则高并发下会排队等待空闲连接POST),否则可能引发重复提交client := &http.Client{
Timeout: 5 * time.Second,
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: 3 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
TLSHandshakeTimeout: 3 * time.Second,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 100,
},
}用 chi、gin 等框架时,中间件链和路由树深度会影响首字节延迟:
http.HandlerFunc 测 baseline 响应时间;再逐个开启对比Logger 中解析 req.Body —— 会 consume body 导致后续 handler 读不到/api/v1/*)会拖慢 trie 匹配,可改用更精确前缀(/api/v1/users/)并分离静态资源路径真正卡住的地方,往往不是你怀疑的那行 db.QueryRow,而是前面某个没设超时的 http.Get,或者一个没人消费的 chan int。