Context
Go 語言 Context:取消信號、截止時間、請求範圍值。
Context 概念

Context 用於在 API 邊界和進程間傳遞截止時間、取消信號和請求範圍值。
何時使用 Context?
- Incoming requests(接收請求)
- Outgoing requests(發送請求)
使用 Context 的標準庫
netdatabase/sqlos/exec
WithCancel
用於手動取消操作。
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go worker(ctx, "worker-1")
time.Sleep(3 * time.Second)
cancel() // 發送取消信號
time.Sleep(time.Second)
}
func worker(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Printf("%s: received cancel signal\n", name)
return
default:
fmt.Printf("%s: working...\n", name)
time.Sleep(500 * time.Millisecond)
}
}
}WithTimeout
在指定時間後自動取消。
func fetchData(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
select {
case <-time.After(3 * time.Second): // 模擬長時間操作
return nil
case <-ctx.Done():
return ctx.Err() // context deadline exceeded
}
}HTTP 請求超時
func fetchURL(url string) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err // 超時會返回 context deadline exceeded
}
defer resp.Body.Close()
return nil
}WithDeadline
在指定時間點自動取消。
func main() {
deadline := time.Now().Add(5 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
select {
case <-time.After(10 * time.Second):
fmt.Println("operation completed")
case <-ctx.Done():
fmt.Println("deadline exceeded:", ctx.Err())
}
}WithValue
在請求間傳遞共享值,常用於傳遞請求 ID、認證資訊等。
type contextKey string
const (
userIDKey contextKey = "userID"
requestIDKey contextKey = "requestID"
)
func main() {
ctx := context.Background()
ctx = context.WithValue(ctx, userIDKey, "user-123")
ctx = context.WithValue(ctx, requestIDKey, "req-456")
handleRequest(ctx)
}
func handleRequest(ctx context.Context) {
userID := ctx.Value(userIDKey).(string)
requestID := ctx.Value(requestIDKey).(string)
fmt.Printf("User: %s, Request: %s\n", userID, requestID)
}避免使用 WithValue 傳遞可選參數。Context value 應只用於跨 API 和進程邊界的請求範圍數據。
Context 傳播模式
鏈式傳遞
func main() {
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
if err := serviceA(ctx); err != nil {
log.Fatal(err)
}
}
func serviceA(ctx context.Context) error {
// 可以添加自己的超時
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
return serviceB(ctx)
}
func serviceB(ctx context.Context) error {
// 使用父 context 的截止時間
select {
case <-ctx.Done():
return ctx.Err()
default:
// 處理業務邏輯
return nil
}
}資料庫操作
func queryUser(ctx context.Context, db *sql.DB, id int) (*User, error) {
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
row := db.QueryRowContext(ctx, "SELECT * FROM users WHERE id = ?", id)
var user User
if err := row.Scan(&user.ID, &user.Name); err != nil {
return nil, err
}
return &user, nil
}Links
Timeouts
Timeouts 對於連接外部資源或需要限制執行時間的程式很重要。
- Links: Timeouts Example
Timers
當你想在未來某個時間點執行一次操作時使用。
timer := time.NewTimer(2 * time.Second)
<-timer.C
fmt.Println("Timer fired")
// 可以取消
timer2 := time.NewTimer(time.Second)
go func() {
<-timer2.C
fmt.Println("Timer 2 fired")
}()
timer2.Stop() // 取消 timer- Links: Timers Example
Tickers
當你想以固定間隔重複執行操作時使用。
ticker := time.NewTicker(500 * time.Millisecond)
done := make(chan bool)
go func() {
for {
select {
case <-done:
return
case t := <-ticker.C:
fmt.Println("Tick at", t)
}
}
}()
time.Sleep(2 * time.Second)
ticker.Stop()
done <- true- Links: Tickers Example