mcpField
G
golang-context

golang-context

github.com/samber开发3.7k41.4k 次安装5 天前更新
代码仓库

关于 golang-context

面向 Go 开发者的 context.Context 实践指南,说明如何在请求链路中传播取消信号、期限和请求级数据。内容涵盖上下文创建、值的使用边界及 HTTP、数据库和跨服务场景。

主要功能

  1. 01贯穿请求链路传播 context
  2. 02选择 Background、TODO 与请求上下文
  3. 03管理取消函数和超时
  4. 04安全使用 context 值
  5. 05在 HTTP 与数据库调用中传递 context

适用场景

  1. 01设计支持取消的服务调用
  2. 02为请求设置期限并避免资源泄漏
  3. 03在服务间传递请求元数据
  4. 04排查 context 使用不当

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/samber/cc-skills-golang --skill golang-context

安装后在 Claude Code 中输入/golang-context确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evals/
evals.json
references/
cancellation.md
http-services.md
values-tracing.md

详细介绍

社区默认规则。 明确声明取代 samber/cc-skills-golang@golang-context skill 的公司 skill 优先。

Go context.Context 最佳实践

context.Context 是 Go 用于在 API 边界之间以及 goroutine 之间传递取消信号、截止时间和请求级值的机制。可以把它看作请求的“会话”——它将属于同一工作单元的所有操作关联起来。

最佳实践摘要

  1. 在整个请求生命周期中传递同一个 context:HTTP 处理程序 → 服务 → DB → 外部 API——任何新建 context 的环节,都会在客户端断开后继续工作。
  2. 将 ctx 作为第一个参数,命名为 ctx context.Context——固定的位置让人一眼就能识别支持 context 的 API,也是 linter 检查的内容。
  3. 通过函数参数传递 context,而不是将其存储在 struct 中——struct 的生命周期会超过填充它的请求,因此后续调用可能会复用已取消或属于其他请求的 context。
  4. 传递 context.TODO(),而不是 nil context——nil 会在首次调用 Done() 或 Value() 时触发 panic,而且错误发生的位置离传入它的调用方很远。
  5. 对 WithCancel/WithTimeout/WithDeadline,在所有控制流路径上调用 cancel(),除非 context 和 cancel 函数的所有权被明确返回或转移——如果不调用 cancel(),子 context 会一直附着在父 context 上,并且其计时器会持续占用资源,直到父 context 结束。
  6. 仅在顶层入口点(main、init、测试)创建 context.Background()。在调用链更深处——尤其是在请求处理中途——它会使工作脱离调用方的截止时间和取消信号,这就是下方所示的传播中断。
  7. 当需要 context 但暂时没有时,使用 context.TODO() 作为占位符——这会标记出待后续修复的问题,而不是用看似有意为之的 Background() 将其掩盖。
  8. 将 context 值的键声明为未导出类型——如果使用普通 string 键,两个使用 "user" 的包会在不报错的情况下相互覆盖。
  9. 只在 context 值中携带请求级元数据,绝不携带函数参数——通过 Value() 获取的值会失去编译时类型检查,也不会出现在函数签名中。
  10. 使用 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 会自动取消所有下游工作。

// ✗ Bad — creates a new context, breaking the chain
func (s *OrderService) Create(ctx context.Context, order Order) error {
    return s.db.ExecContext(context.Background(), "INSERT INTO orders ...", order.ID)
}

// ✓ Good — propagates the caller's 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。

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

  • HTTP 服务与服务调用中的上下文 — HTTP 处理程序上下文:使用 r.Context() 实现请求范围内的取消、与中间件集成,并将上下文传播到服务。HTTP 客户端模式:NewRequestWithContext、客户端超时,以及支持上下文感知的重试。数据库操作:始终使用 *Context 变体(QueryContext、ExecContext)以遵守截止期限。

交叉引用

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

使用 Linter 强制检查

许多上下文陷阱都可由 Linter 自动捕获:govet、staticcheck。→ 参阅 samber/cc-skills-golang@golang-lint 技能,了解配置和用法。

评论

支持 Markdown