Context 上下文
context 包用于在 goroutine 树中传递取消信号、超时、截止时间以及请求范围的值,是 Go 微服务和 HTTP 服务的标配。
核心接口
1 2 3 4 5 6
| type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key any) any }
|
创建 Context
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import "context"
ctx := context.Background()
ctx = context.TODO()
ctx, cancel := context.WithCancel(parent) defer cancel()
ctx, cancel := context.WithTimeout(parent, 5*time.Second) defer cancel()
deadline := time.Now().Add(10 * time.Second) ctx, cancel := context.WithDeadline(parent, deadline) defer cancel()
|
取消传播
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| func doWork(ctx context.Context) error { for { select { case <-ctx.Done(): return ctx.Err() default: time.Sleep(100 * time.Millisecond) } } }
func main() { ctx, cancel := context.WithCancel(context.Background()) go doWork(ctx)
time.Sleep(time.Second) cancel() }
|
HTTP 请求中的 Context
http.Request 自带 Context,应在调用链中传递:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| func handler(w http.ResponseWriter, r *http.Request) { ctx := r.Context()
result, err := queryDB(ctx, "SELECT ...") if err != nil { if errors.Is(err, context.DeadlineExceeded) { http.Error(w, "timeout", http.StatusGatewayTimeout) return } http.Error(w, err.Error(), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(result) }
func queryDB(ctx context.Context, sql string) (Result, error) { row := db.QueryRowContext(ctx, sql) }
|
传递请求范围值
1 2 3 4 5 6 7 8 9 10 11 12
| type ctxKey string
const traceIDKey ctxKey = "traceID"
func withTraceID(ctx context.Context, id string) context.Context { return context.WithValue(ctx, traceIDKey, id) }
func getTraceID(ctx context.Context) string { v, _ := ctx.Value(traceIDKey).(string) return v }
|
注意:WithValue 仅用于请求链路追踪等场景,不要用来传可选参数,key 应使用自定义类型避免冲突。
最佳实践
- Context 作为函数第一个参数:
func Foo(ctx context.Context, ...)
- 不要存储 Context 到 struct,每次调用传入
- 不要传 nil Context,不确定时用
context.TODO()
- Value 不要滥用,只放请求元数据(traceId、userId)
- 父 Context 取消时,子 Context 自动取消
小结
context 统一管理 goroutine 生命周期
WithCancel / WithTimeout / WithDeadline 三种常用变体
- HTTP、数据库、RPC 调用都应传递 Context,支持超时与优雅退出