Go Web 服务中应为每个 HTTP 请求创建带超时的 context,避免复用全局 context;在 handler 入口或中间件中使用 context.WithTimeout 派生新 context,并透传至 DB、HTTP 等所有阻塞操作。

在 Go Web 服务中,context.Context 是控制请求生命周期、传播取消信号和传递超时的核心机制。合理使用 context 能显著降低因长等待、资源未释放或 goroutine 泄漏带来的性能开销,尤其在高并发、链路调用深的场景下效果明显。
为每个 HTTP 请求创建带超时的 context
不要复用全局或长期存活的 context;应在 handler 入口立即派生新 context,并设置合理的截止时间。超时值应略大于下游依赖(如 DB、RPC)的预期耗时,避免过早中断,也防止无限等待。
- 用
context.WithTimeout(ctx, 5*time.Second)替代无限制的context.Background() - 若需统一超时策略,可在中间件中注入,例如:
r.Use(func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second); defer cancel(); r = r.WithContext(ctx); h.ServeHTTP(w, r) }) }) - 注意:超时后,
ctx.Err()返回context.DeadlineExceeded,下游函数应检查并快速退出
将 context 透传到所有阻塞操作中
任何可能挂起的调用——数据库查询、HTTP 客户端请求、channel 接收、锁等待——都应接受 context 并响应取消。标准库(如 net/http.Client、database/sql)已原生支持 context;自定义函数也需显式接收并监听。
- HTTP 请求示例:
resp, err := http.DefaultClient.Do(req.WithContext(ctx)) - SQL 查询示例:
rows, err := db.QueryContext(ctx, "SELECT ...") - 自定义函数需主动轮询:
select { case - 避免在 goroutine 中忽略 context:启动子 goroutine 时务必传入派生 context,并在退出前调用
cancel()(若需)
避免 context 取消后的资源泄漏
context 取消仅发送信号,不自动释放文件句柄、连接、goroutine 等资源。必须在业务逻辑中显式清理,否则会持续占用内存或连接数。
立即学习“go语言免费学习笔记(深入)”;
- 使用
defer cancel()配合WithTimeout或WithCancel,确保无论成功失败都释放 cancel 函数 - 对数据库连接、HTTP 响应体等,用
defer resp.Body.Close()或defer rows.Close(),且这些操作应在 context 取消后仍能安全执行 - 若启动了后台 goroutine 处理异步任务,应在
ctx.Done()触发时退出循环并关闭相关 channel
慎用 context.WithValue 传递业务数据
WithValue 仅适合传递请求范围的元数据(如 traceID、用户身份),不应替代函数参数传递核心业务字段。过度使用会增加隐式依赖,降低可读性与可测性,也带来额外分配开销。
- 推荐做法:handler 解析必要参数后,作为普通参数传给业务函数
- 若必须用
WithValue,请定义明确的 key 类型(如type userIDKey struct{}),避免字符串 key 冲突 - 注意:value 不参与 cancel/timeout 控制,纯属“附带信息”,勿用于控制流程分支










