golang-context

golang-context

热门

Go 语言中惯用的 context.Context 用法——跨 API 边界传播、取消、超时和截止时间、请求作用域的值、用于超出请求存活期的后台工作的 context.WithoutCancel。适用于设计跨层级的 context 传播、调试泄漏或未过期的 context、在 context.Background/TODO/WithoutCancel 之间选择、或在 context 中存储值。不适用于仅将 ctx 作为第一个参数接受的代码。

2261Star
150Fork
更新于 2026/6/6
SKILL.md
只读
名称
golang-context
描述

Go 语言中惯用的 context.Context 用法——跨 API 边界传播、取消、超时和截止时间、请求作用域的值、用于超出请求存活期的后台工作的 context.WithoutCancel。适用于设计跨层级的 context 传播、调试泄漏或未过期的 context、在 context.Background/TODO/WithoutCancel 之间选择、或在 context 中存储值。不适用于仅将 ctx 作为第一个参数接受的代码。

社区默认。 明确取代 samber/cc-skills-golang@golang-context 技能的公司技能具有优先权。

Go context.Context 最佳实践

context.Context 是 Go 用于跨 API 边界和 goroutine 传播取消信号、截止时间和请求作用域值的机制。可以将其视为请求的“会话”——它将属于同一工作单元的每个操作联系在一起。

最佳实践总结

  1. 同一个 context 必须贯穿整个请求生命周期:HTTP 处理器 → 服务 → 数据库 → 外部 API
  2. ctx 必须是第一个参数,命名为 ctx context.Context
  3. 永远不要将 context 存储在结构体中——通过函数参数显式传递
  4. 永远不要传递 nil context——如果不确定,使用 context.TODO()
  5. 对于 WithCancel/WithTimeout/WithDeadline,必须在所有控制流路径上调用 cancel(),除非 context 和取消函数的所有权被显式返回或转移
  6. context.Background() 只能在顶层使用(main、init、测试)
  7. 使用 context.TODO() 作为占位符,当你知道需要 context 但还没有时
  8. 永远不要在请求路径中间创建新的 context.Background()
  9. Context 值的键必须是未导出的类型,以防止冲突
  10. Context 值只能携带请求作用域的元数据——绝不能是函数参数
  11. 使用 context.WithoutCancel(Go 1.21+)在生成必须比父请求存活更久的后台工作时

创建 Context

场景 使用
入口点(main、init、测试) context.Background()
函数需要 context 但调用者尚未提供 context.TODO()
在 HTTP 处理器内部 r.Context()
需要取消控制 context.WithCancel(parentCtx)
需要截止时间/超时 context.WithTimeout(parentCtx, duration)

Context 传播:核心原则

最重要的规则:将同一个 context 传播到整个调用链。正确传播时,取消父 context 会自动取消所有下游工作。

// ✗ 错误——创建新的 context,断开了链
func (s *OrderService) Create(ctx context.Context, order Order) error {
    return s.db.ExecContext(context.Background(), "INSERT INTO orders ...", order.ID)
}

// ✓ 正确——传播调用者的 context
func (s *OrderService) Create(ctx context.Context, order Order) error {
    return s.db.ExecContext(ctx, "INSERT INTO orders ...", order.ID)
}

深入探讨

  • 取消、超时与截止时间——取消如何传播:WithCancel 用于手动取消,WithTimeout 用于在持续时间后自动取消,WithDeadline 用于绝对时间截止。在并发代码中监听(<-ctx.Done())的模式,AfterFunc 回调,以及 WithoutCancel 用于必须比父请求存活更久的操作(例如审计日志)。

  • Context 值与跨服务追踪——安全的 context 值模式:未导出的键类型以防止命名空间冲突,何时使用 context 值(请求 ID、用户 ID)与函数参数。追踪上下文传播:OpenTelemetry 追踪头、用于日志聚合的关联 ID,以及跨服务边界编组/解组 context。

  • HTTP 服务器与服务调用中的 Context——HTTP 处理器 context:r.Context() 用于请求作用域的取消,中间件集成,以及传播到服务。HTTP 客户端模式:NewRequestWithContext,客户端超时,以及具有 context 感知的重试。数据库操作:始终使用 *Context 变体(QueryContextExecContext)以尊重截止时间。

交叉引用

  • → 参见 samber/cc-skills-golang@golang-concurrency 技能,了解使用 context 的 goroutine 取消模式
  • → 参见 samber/cc-skills-golang@golang-database 技能,了解 context 感知的数据库操作(QueryContext、ExecContext)
  • → 参见 samber/cc-skills-golang@golang-observability 技能,了解使用 OpenTelemetry 的追踪上下文传播
  • → 参见 samber/cc-skills-golang@golang-design-patterns 技能,了解超时和弹性模式

使用 Linter 强制执行

许多 context 陷阱可以通过 linter 自动捕获:govetstaticcheck。→ 参见 samber/cc-skills-golang@golang-lint 技能了解配置和使用。