1、Context 是什么?
context.Context 是 Go 用来控制协程生命周期、超时、取消信号的机制。 它的主要作用是:在多个 协程之间传递取消信号、超时时间、以及请求相关的数据。就像在复杂的系统中,你需要一个“指挥官”来告诉各个工人何时开始、何时停止工作,Context 就是那个“指挥官”。
2、Context 的常见用途
2.1. 超时控制
当一个请求超过设定时间仍未完成,可以自动取消,防止系统资源被长期占用。
2.2. 取消信号传播
如果上层任务取消,所有关联的子 Goroutine 都能立即收到信号,及时停止。
2.3. 传递请求范围内的元数据
例如:用户ID、Trace ID、Token等,可在整个请求链中共享。
3、Context 的多种创建方式
// 1 通常用于main或测试
ctx := context.Background()
// 2 不确定用哪个时,使用的占位符
ctx := context.TODO()
// 3 创建可取消的子Context
ctx,cancel := context.WithCancel(context.Background())
// 4 超时自动取消
ctx,cancel := context.WithTimeout(context.Background(),5*time.Second)
// 5 到指定时间自动取消
ctx,cancel := context.WithDeadline(context.Background(), time.Now().Add(2*time.Second))
// 6 携带键值数据
ctx := context.WithValue(context.Background(), "userID", 123)
4、实际使用示例
4.1:请求超时控制
package main
import (
"context"
"fmt"
"time"
)
func main() {
// 超时自动取消
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
ch := make(chan string)
go func() {
time.Sleep(5 * time.Second)
ch <- "任务完成"
}()
select {
case <-ctx.Done():
fmt.Println("超时:", ctx.Err())
case result := <-ch:
fmt.Println(result)
}
}
输出如图:
4.2:取消信号的传播
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Println(name, "收到取消信号,退出")
return
default:
fmt.Println(name, "正在工作中...")
time.Sleep(500 * time.Millisecond)
}
}
}
func main() {
// 创建可取消的子Context
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx, "worker-1")
go worker(ctx, "worker-2")
go worker(ctx, "worker-3")
time.Sleep(2 * time.Second)
fmt.Println("主程序:任务取消")
// 主动取消,发送取消信号
cancel()
time.Sleep(1 * time.Second)
}
输出如图:
4.3 传递请求范围内的元数据
package main
import (
"context"
"fmt"
)
// 定义上下文键的类型(避免不同包的 key 冲突)
type ctxKey string
const (
CtxUserID ctxKey = "userID"
CtxTraceID ctxKey = "traceID"
)
// 模拟业务逻辑层
func serviceCall(ctx context.Context) {
// 从 Context 中获取值
userID := ctx.Value(CtxUserID)
traceID := ctx.Value(CtxTraceID)
fmt.Println("服务处理中...")
fmt.Printf("TraceID: %v | UserID: %v\n", traceID, userID)
// 模拟调用数据库操作
dbQuery(ctx)
}
// 模拟数据库查询
func dbQuery(ctx context.Context) {
fmt.Println("数据库查询中...")
fmt.Printf("当前 TraceID: %v\n", ctx.Value(CtxTraceID)) // 从Context中取值
}
func main() {
// 创建根 Context
ctx := context.Background()
// 在 Context 中附加元数据
ctx = context.WithValue(ctx, CtxUserID, 1)
ctx = context.WithValue(ctx, CtxTraceID, "TRACE-ID-1")
// 将带数据的 Context 传递下去
serviceCall(ctx)
}
输出如图:
5、在网络服务中的重要性
在构建 Web 服务或微服务时,Context 通常用于:
-
控制数据库查询或 API 请求超时;
-
请求被用户取消时,自动停止后台任务;
-
分布式追踪(Tracing)中传递 TraceID;
-
限制协程泄漏(防止任务永远不退出)。
想象你是一个登山队的队长,带领多个队员(协程)一起登山。 Context 就像你的“对讲机”:
你说“下山!”(cancel())——所有队员都会立刻收到信号停止行动;
你设定“如果两小时内没到顶,就撤!”(WithTimeout)——时间到了自动通知;
你可以在对讲机里传递信息(WithValue)——比如“目标坐标”或“队伍编号”。
没有 Context,队伍可能迷路;有了 Context,整个队伍协同高效,进退有度。
源码地址
1、公众号“Codee君”回复“源码”获取源码
如果您喜欢这篇文章,请您(点赞、分享、亮爱心),万分感谢!