必须用 http.newrequestwithcontext(ctx) 透传超时 context 给下游 http 调用,避免 req.withcontext(ctx) 被 transport 忽略;gin.timeout 无法覆盖 dns/tcp 等底层阻塞,需配 http.transport 各阶段超时;grpc 超时须每次调用传 deadline ctx,漏传即永不超时。

gin handler 里怎么传 context 超时给下游 HTTP 调用
必须用 http.NewRequestWithContext(ctx, method, url, body),不能先 http.NewRequest() 再调 req.WithContext(ctx)。后者在某些 Transport 实现下会被忽略,导致超时不生效。
- 下游 HTTP 调用(比如调其他微服务)必须显式把 handler 接收的
ctx透传过去,而不是用context.Background()或硬编码新context.WithTimeout() - 如果 handler 内部启动子 goroutine 去发请求,必须把
ctx传进去,并在 goroutine 中监听ctx.Done() - 错误处理要区分
context.DeadlineExceeded和网络错误:前者说明是主动取消,不应再读resp.Body;后者可能需要重试或降级
为什么 gin.Timeout 中间件不能替代 transport 层超时
gin.Timeout 中间件只控制 handler 执行时间,对 DNS 解析、TCP 连接、TLS 握手这些底层阻塞点完全无效。你设了 1s 超时,但 DNS 卡住 5s,请求照样卡住,且 ctx.Err() 一直为 nil。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 真正防卡死,得靠
http.Transport的DialContext(含 DNS + TCP)、TLSHandshakeTimeout、ResponseHeaderTimeout -
http.Client.Timeout是兜底总耗时,建议设为 ≤30s,但它不覆盖前置阶段,也不能替代 transport 级配置 - 微服务间调用频繁,transport 复用很关键,必须提前配好,而不是每次 new client
gRPC 客户端调用超时为什么总失效
因为 grpc.Dial() 里的 grpc.WithTimeout 已废弃,且完全不生效。gRPC 超时是 per-RPC 的,不是 per-connection。
- 每次调用都必须传带 deadline 的
ctx:client.GetUser(ctx, req),漏一次就等于这次调用永不超时 - 流式 RPC 更要注意:
Recv()和Send()也应套独立context.WithTimeout,否则单次读写卡住会拖垮整个 stream - 服务端可通过
ctx.Deadline()获取客户端截止时间,并在 DB 查询、缓存操作中主动检查ctx.Done()
cancel() 忘调会导致什么
不是立刻 panic,而是定时器和 goroutine 持续泄漏。每次 context.WithTimeout() 都启一个 time.Timer,不 cancel() 就不会回收,积压后 pprof 里能看到大量 stuck 在 runtime.netpoll 的 goroutine。
- 常见错误:
ctx, _ := context.WithTimeout(parent, d)—— 直接丢弃cancel函数 - 正确写法必须是
ctx, cancel := context.WithTimeout(parent, d),并在作用域末尾defer cancel() - 如果有多个 return 分支(比如 error early exit),
defer会失效,得在每个分支前显式cancel()
gin.Timeout 或 http.Client.Timeout;还有就是 gRPC 调用漏传 ctx,以为连上就安全了。这两类问题在高并发压测时才会集中暴露。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










