Skip to content
Go 并发与运行时 · 第 3 篇 / 共 4 篇
领域编程与框架
专题Go 专题
当前序列Go 并发与运行时
阅读位置第 3 篇 / 共 4 篇当前专题第 1 个序列 / 共 3 个序列

context 的超时、取消和传播边界

先说结论

  • context 的核心价值不是“传参数”,而是传递取消信号、截止时间和最小上下文
  • 一次请求链路里的 goroutine、数据库调用、RPC 调用最好共用一条可取消链路
  • context.Value 只适合放很少量、请求级的元信息,不适合当万能参数包

一、context 到底解决什么问题

Go 里一旦并发多起来,最容易出的问题不是“怎么启动 goroutine”,而是:

  • 请求都结束了,后台 goroutine 还在跑
  • 上游已经超时了,下游还在继续打数据库
  • 多层调用里超时和取消没统一

context 解决的就是这三件事:

  • 取消传播
  • 超时截止
  • 请求级上下文透传

二、超时和取消分别是什么

1. 超时

超时更像“这件事最多做多久”。
常见于:

  • HTTP 请求
  • 数据库查询
  • 下游 RPC

2. 取消

取消更像“上游已经不要结果了,后面一起停”。
比如:

  • 用户主动断开请求
  • 网关已经超时返回
  • 上级任务提前结束

这两者经常一起出现,但语义不完全一样。

三、一个更稳的使用顺序

更推荐按这条链路理解:

  1. 请求入口生成根 context。
  2. 往下游调用继续传递这条 context。
  3. 真正可能阻塞的操作都监听 Done()
  4. defer cancel() 及时释放资源。

这条链路的关键是:
不要只在入口设超时,却让中间层和下游完全感知不到。

四、最常见的几个坑

1. 把 context 存到 struct 里长期复用

context 是请求级对象,不适合当服务级状态保存。

2. 把业务参数全塞进 context.Value

比如用户对象、订单详情、复杂配置都往里丢,后面阅读和维护都会很差。

3. 创建了超时 context 却不 cancel

这会让定时器和资源比预期活得更久。

4. 只传 context,不真正响应取消

函数签名里有 ctx context.Context,但内部根本不检查 Done(),那就只是“形式上透传”。

五、哪些信息适合放进 context

更适合放的通常是:

  • traceId
  • requestId
  • 用户 ID 这类轻量身份信息
  • 语言、租户这类请求级元信息

不适合放的通常是:

  • 大对象
  • 可变业务状态
  • 配置集合
  • 当次流程里到处共享的临时缓存

六、排查超时泄漏时先看什么

如果你怀疑 goroutine 没有随着请求一起退出,优先看:

  1. 入口是否真的设置了超时或取消。
  2. 下游函数是否持续透传了同一条 context。
  3. 阻塞点是否监听了 Done()
  4. 数据库 / HTTP 客户端是否真正支持 context 取消。

很多“Go 服务 goroutine 越跑越多”的问题,本质上不是 goroutine 本身,而是取消链路断了。

总结

Go 里的 context 最重要的不是语法,而是边界:它负责取消、超时和请求级上下文,不负责承载复杂业务数据。只要把这条边界守住,很多超时、泄漏和下游悬挂问题都会少很多。

延伸阅读相关文章优先当前专题,再补跨专题关联。
同一序列 · 顺着当前主线继续读Go 里的 Mutex、RWMutex、Atomic 怎么选适合把 goroutine、channel、context 和锁选择放到一起理解。Go 专题 · Go 并发与运行时同一序列 · 回看前文会更完整有缓冲 channel 和无缓冲 channel 怎么选适合把 goroutine、channel、context 和锁选择放到一起理解。Go 专题 · Go 并发与运行时同专题其他序列 · Go 并发模型与服务工程化context 传递截止时间和链路信息的边界适合把 goroutine 生命周期、context 边界、接口抽象和优雅停机放回 Go 服务实践里思考。Go 专题 · Go 并发模型与服务工程化同专题其他序列 · Go 工程实践与内存治理Go 服务项目目录怎么组织更稳适合把逃逸分析、GC、错误处理和项目布局放在一条工程主线上看。Go 专题 · Go 工程实践与内存治理跨专题关联 · 共享标签:线上排障、案例排障磁盘打满后为什么删除文件不一定立刻生效适合把 Redis 抖动、MySQL 连接打满、MQ 积压、ES 发黄、ClickHouse 合并堆积和磁盘打满放在同一条基础设施排障主线上看。数据与基础设施排障专题 · 数据与基础设施故障的分层排查跨专题关联 · 共享标签:线上排障、案例排障大 Header、buffer、timeout 问题怎么排查适合把 location 匹配、缓冲区和负载均衡放在一起看。容器与站点部署专题 · Nginx 进阶路由与代理治理
继续阅读Go 并发与运行时当前序列第 3 篇 / 共 4 篇当前专题第 1 个序列 / 共 3 个序列
往前看
上一篇有缓冲 channel 和无缓冲 channel 怎么选回到当前序列上一章上一序列MyBatis 性能与写入治理从第 1 篇开始:MyBatis 分页插件和 Count 优化
往后看
下一篇Go 里的 Mutex、RWMutex、Atomic 怎么选继续当前序列下一章下一序列Go 工程实践与内存治理从第 1 篇开始:Go 逃逸分析怎么影响性能

把零散经验整理成可查、可复用、可持续更新的企业级知识门户