golang-context

golang-context

熱門

在 Golang 中慣用地使用 context.Context — 透過 API 邊界傳播、取消、逾時與截止時間、請求範圍的值、以及 context.WithoutCancel 用於超出請求生命週期的背景工作。適用於設計跨層級的 context 傳播、偵測洩漏或未過期的 context、選擇 context.Background/TODO/WithoutCancel,或在 context 中儲存值。不適用於僅將 ctx 作為第一個參數的程式碼。

2261星標
150分支
更新於 2026/6/6
SKILL.md
readonlyread-only
name
golang-context
description

在 Golang 中慣用地使用 context.Context — 透過 API 邊界傳播、取消、逾時與截止時間、請求範圍的值、以及 context.WithoutCancel 用於超出請求生命週期的背景工作。適用於設計跨層級的 context 傳播、偵測洩漏或未過期的 context、選擇 context.Background/TODO/WithoutCancel,或在 context 中儲存值。不適用於僅將 ctx 作為第一個參數的程式碼。

社群預設。 若公司技能明確取代 samber/cc-skills-golang@golang-context 技能,則以公司技能為準。

Go context.Context 最佳實務

context.Context 是 Go 用來在 API 邊界與 goroutine 之間傳遞取消訊號、截止時間以及請求範圍值的機制。可以把它想像成請求的「工作階段」——它將屬於同一個工作單元的所有操作串聯在一起。

最佳實務摘要

  1. 同一個 context 必須在整個請求生命週期中傳播:HTTP handler → service → DB → 外部 API
  2. ctx 必須是第一個參數,命名為 ctx context.Context
  3. 絕對不要將 context 儲存在 struct 中——應透過函式參數明確傳遞
  4. 絕對不要傳遞 nil context——如果不確定,請使用 context.TODO()
  5. 對於 WithCancel/WithTimeout/WithDeadline,必須在所有控制流程路徑上呼叫 cancel(),除非明確回傳或轉移 context 與 cancel 函式的所有權
  6. context.Background() 只能在頂層使用(main、init、測試)
  7. 使用 context.TODO() 作為佔位符,當你知道需要 context 但尚未取得時
  8. 絕對不要在請求路徑中間建立新的 context.Background()
  9. Context 值的鍵必須使用未匯出的型別,以避免碰撞
  10. Context 值只能攜帶請求範圍的中繼資料——絕對不能作為函式參數
  11. 使用 context.WithoutCancel(Go 1.21+)在產生必須比父請求存活更久的背景工作時

建立 Context

情境 使用
進入點(main、init、測試) context.Background()
函式需要 context 但呼叫者尚未提供 context.TODO()
在 HTTP handler 內部 r.Context()
需要取消控制 context.WithCancel(parentCtx)
需要截止時間/逾時 context.WithTimeout(parentCtx, duration)

Context 傳播:核心原則

最重要的規則:在同一個呼叫鏈中傳遞同一個 context。當你正確傳播時,取消父 context 會自動取消所有下游工作。

// ✗ 不好——建立新的 context,中斷了鏈結
func (s *OrderService) Create(ctx context.Context, order Order) error {
    return s.db.ExecContext(context.Background(), "INSERT INTO orders ...", order.ID)
}

// ✓ 好——傳遞呼叫者的 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 用於必須比父請求存活更久的操作(例如稽核日誌)。

  • Context 值與跨服務追蹤 — 安全的 context 值模式:使用未匯出的鍵型別避免命名空間碰撞,何時使用 context 值(請求 ID、使用者 ID)與函式參數。追蹤 context 傳播:OpenTelemetry 追蹤標頭、用於日誌聚合的關聯 ID,以及在服務邊界之間序列化/反序列化 context。

  • HTTP 伺服器與服務呼叫中的 Context — HTTP handler context:r.Context() 用於請求範圍的取消、中介軟體整合,以及傳播到服務。HTTP 客戶端模式:NewRequestWithContext、客戶端逾時,以及具 context 感知的重試。資料庫操作:一律使用 *Context 變體(QueryContextExecContext)以遵守截止時間。

交叉參考

  • → 參閱 samber/cc-skills-golang@golang-concurrency 技能,了解使用 context 的 goroutine 取消模式
  • → 參閱 samber/cc-skills-golang@golang-database 技能,了解具 context 感知的資料庫操作(QueryContext、ExecContext)
  • → 參閱 samber/cc-skills-golang@golang-observability 技能,了解使用 OpenTelemetry 的追蹤 context 傳播
  • → 參閱 samber/cc-skills-golang@golang-design-patterns 技能,了解逾時與韌性模式

使用 Linter 強制執行

許多 context 陷阱可由 linter 自動偵測:govetstaticcheck。→ 參閱 samber/cc-skills-golang@golang-lint 技能以了解設定與使用方式。