context.WithTimeout 必须配对调用 cancel(),否则 timer 和 goroutine 持续泄漏;HTTP/gRPC 超时需传入底层可取消环节,子 context 超时不能突破父 context 剩余时间。

context.WithTimeout 必须配对调用 cancel(),否则定时器和 goroutine 持续泄漏
不调用 cancel() 不会立刻报错,但每次调用 context.WithTimeout() 都会启动一个 time.Timer,它不会自动回收。哪怕超时已触发,timer 仍驻留内存,goroutine 无法被 GC 清理,积压后拖慢整个服务。
常见错误写法:ctx, _ := context.WithTimeout(parent, 3*time.Second) —— 直接丢弃 cancel 函数。
- 正确姿势始终是:
ctx, cancel := context.WithTimeout(parent, timeout),并在作用域末尾defer cancel() - 如果函数有多个
return分支(比如 error early exit),defer会失效,必须在每个分支前显式调用cancel() -
cancel()是幂等的,重复调用无害,但掩盖了控制流缺陷;建议把主逻辑包进匿名函数统一出口,或用if err != nil { cancel(); return }显式兜底
HTTP 请求超时“不生效”?问题不在 context,而在没传到底层可取消环节
context.WithTimeout 只控制你代码里显式监听 ctx.Done() 的部分,DNS 解析、TCP 连接、TLS 握手这些底层阻塞阶段默认不响应它——尤其在 Go 1.19 之前。
典型现象:设了 100ms 超时,但请求卡了 3 秒才返回,且 ctx.Err() 一直为 nil。
- 必须用
http.NewRequestWithContext(ctx, ...)创建请求,而不是req.WithContext(ctx)后再传给client.Do()(某些 transport 实现可能忽略后者) - 配置
http.Transport:启用DialContext控制建连、TLSHandshakeTimeout控制握手、ResponseHeaderTimeout控制响应头到达时间 - 避免混用
http.Client.Timeout和 context:前者内部新建子 context,可能覆盖你传入的 deadline
gRPC 客户端超时必须 per-RPC 绑定,不是 per-connection
gRPC 的超时是按每次 RPC 调用生效的,即使复用同一个 *grpc.ClientConn,漏掉一次 ctx 传入,这次调用就等于永不超时。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
常见误区:grpc.WithTimeoutDial 选项早已废弃,对 RPC 调用完全无效;用 context.Background() 初始化 client,导致所有调用失去超时控制。
- 每次
client.SomeMethod(ctx, req)或client.SomeStream(ctx)都必须传入带超时的ctx - 流式 RPC 中,仅在
NewStream时设超时不够,Recv()和Send()也应单独套更细粒度的 context(例如用context.WithTimeout包裹单次读写) - 服务端可通过
ctx.Deadline()获取客户端传递的截止时间,并在耗时操作中主动检查ctx.Done()
子 context 超时不能突破父 context 剩余时间
这是级联取消的硬约束:父 ctx 剩余 2s,你用 context.WithTimeout(parent, 10*time.Second) 创建子 ctx,它实际最多活 2s —— timer 会被重设为父 ctx 剩余时间。
微服务链路中,上游设了 5s 总超时,下游再设 10s 没意义;中间件若为每个 handler 单独设 10s,但上游已只剩 1s,那这个 handler 实际只有 1s 可用。
- 不要假设子 context 能“延长”总耗时,它只能缩短或继承
- 跨服务传递时,gRPC 和 HTTP 都会把 deadline 编码进 metadata 自动透传,服务端无需手动解析,直接用
ctx即可 - 真正容易被忽略的是:handler 内部的数据库查询、缓存访问、第三方 API 调用,都得显式接收并传递该
ctx,否则超时信号就断在中间了
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










