context.WithTimeout通过ctx.Done()主动取消goroutine,需确保HTTP、DB、channel等操作响应取消信号;http.Client需分层设超时,select必须包裹channel操作,超时后须手动清理资源。
用 context.WithTimeout 控制 goroutine 生命周期
超时不是靠 time.Sleep 等出来的,而是通过 context.Context 主动取消 goroutine。核心是让所有可能阻塞的操作(如 HTTP 请求、数据库查询、channel 读写)能响应 ctx.Done() 信号。
常见错误是只对主逻辑加超时,但子调用(比如第三方库的 http.Client.Do)没传入 context,导致超时失效。
-
context.WithTimeout返回ctx和cancel,必须在函数退出前调用cancel(),否则可能泄漏 goroutine - HTTP 客户端需显式使用
http.NewRequestWithContext(ctx, ...),默认的http.Get不支持 context - 自定义 channel 操作要配合
select+ctx.Done(),不能直接
HTTP 请求超时必须分层设置
Go 的 http.Client 有三个关键超时字段,它们作用不同,漏设任一都可能让整体超时不生效:
-
Timeout:整个请求从开始到响应 body 读完的总时间(含 DNS、连接、TLS、发送、等待首字节、接收 body) -
Transport.Timeout:已弃用,不要用 -
Transport.DialContext+Transport.TLSClientConfig:需单独控制连接建立和 TLS 握手时间 - 推荐组合:
Timeout设为业务总超时,再用Transport中的DialContext和ResponseHeaderTimeout做细粒度约束
示例中若只设 client.Timeout = 5 * time.Second,但 DNS 解析卡住 10 秒,请求仍会等满 10 秒才失败——因为 DNS 超时由底层 net.Resolver 控制,默认无限制。
select 配合 ctx.Done() 处理 channel 超时
goroutine 间通信常依赖 channel,但 是阻塞操作,必须用 select 包裹才能响应超时。