← 行间 技术 / Go
技术

Go 的 context 到底在传播什么

context.Context 是 Go 里被误用最多的类型之一。它看起来像个能往里塞任何东西的口袋,于是很多代码就真的把它当参数口袋用了。但它真正要传播的只有两样东西:取消信号请求作用域内的元数据

取消是一棵树

每次 context.WithCancelWithTimeout 都会挂出一个子节点,父节点取消时,整棵子树一起收到信号。这棵树的形状应该和你的调用图一致:一个 HTTP 请求进来,派生出三个下游调用,就是一个根加三个叶子。

ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
defer cancel()

defer cancel() 不是可选项。忘了它,即使请求早就返回,定时器和它引用的资源也要等到超时才释放——这是最常见的 context 泄漏。

值传递的红线

context.WithValue 应该只放请求作用域的数据:trace id、用户身份、租户信息。判断标准很简单——如果这个值换一个请求就不一样,它可以进 context;如果它是配置、依赖、可选参数,就该走函数签名。

context 里的值是隐式依赖。隐式依赖越多,函数就越难被单独理解。

不要存起来

context 是随请求走的,生命周期和请求一致。把它塞进 struct 字段长期持有,等于让一个短命的东西被一个长命的东西引用,取消语义立刻就乱了。需要长期取消能力,就为那个长命对象单独建一个 context。

记住这三条,context 就从「玄学」变回了它本来的样子:一根贯穿调用树的信号线。