Context包

Context基础

Context用于在API边界和进程之间传递截止时间、取消信号和其他请求范围的值。

package main

import (
    "context"
    "fmt"
    "time"
)

func main() {
    // 创建根context
    ctx := context.Background()
    
    // 创建带取消功能的context
    ctx, cancel := context.WithCancel(ctx)
    defer cancel()
    
    // 启动goroutine
    go func() {
        for {
            select {
            case <-ctx.Done():
                fmt.Println("Context cancelled")
                return
            default:
                fmt.Println("Working...")
                time.Sleep(time.Second)
            }
        }
    }()
    
    // 3秒后取消context
    time.Sleep(3 * time.Second)
    cancel()
}

Context超时控制

使用WithTimeout或WithDeadline创建带超时的Context。

package main

import (
    "context"
    "fmt"
    "time"
)

func longRunningTask(ctx context.Context) error {
    for {
        select {
        case <-ctx.Done():
            return ctx.Err()
        default:
            fmt.Println("Task running...")
            time.Sleep(time.Second)
        }
    }
}

func main() {
    // 创建5秒超时的context
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    
    // 执行任务
    err := longRunningTask(ctx)
    if err != nil {
        fmt.Println("Task error:", err)
    }
}

Context值传递

使用WithValue在Context中传递请求范围的值。

package main

import (
    "context"
    "fmt"
)

type key string

const (
    userKey key = "user"
    roleKey key = "role"
)

func processRequest(ctx context.Context) {
    // 从context获取值
    if user, ok := ctx.Value(userKey).(string); ok {
        fmt.Println("User:", user)
    }
    if role, ok := ctx.Value(roleKey).(string); ok {
        fmt.Println("Role:", role)
    }
}

func main() {
    // 创建带值的context
    ctx := context.WithValue(context.Background(), userKey, "john")
    ctx = context.WithValue(ctx, roleKey, "admin")
    
    processRequest(ctx)
}

Context在HTTP服务中的应用

在HTTP服务中使用Context处理请求超时和取消。

package main

import (
    "context"
    "fmt"
    "net/http"
    "time"
)

func handler(w http.ResponseWriter, r *http.Request) {
    // 创建带超时的context
    ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
    defer cancel()
    
    // 创建结果channel
    resultCh := make(chan string)
    
    // 在goroutine中处理请求
    go func() {
        // 模拟耗时操作
        time.Sleep(3 * time.Second)
        resultCh <- "Operation completed"
    }()
    
    // 等待结果或超时
    select {
    case result := <-resultCh:
        fmt.Fprintf(w, result)
    case <-ctx.Done():
        http.Error(w, "Request timeout", http.StatusGatewayTimeout)
    }
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

Context在数据库操作中的应用

使用Context控制数据库操作的超时和取消。

package main

import (
    "context"
    "database/sql"
    "fmt"
    "time"
)

func queryWithTimeout(ctx context.Context, db *sql.DB) error {
    // 创建带超时的context
    ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
    defer cancel()
    
    // 执行查询
    rows, err := db.QueryContext(ctx, "SELECT * FROM users")
    if err != nil {
        return err
    }
    defer rows.Close()
    
    // 处理结果
    for rows.Next() {
        // 处理行数据
    }
    
    return rows.Err()
}

func main() {
    // 创建根context
    ctx := context.Background()
    
    // 模拟数据库连接
    var db *sql.DB
    
    // 执行查询
    if err := queryWithTimeout(ctx, db); err != nil {
        fmt.Println("Query error:", err)
    }
}

Context在微服务中的应用

在微服务架构中使用Context传递请求ID和跟踪信息。

package main

import (
    "context"
    "fmt"
    "time"
)

type RequestID string

func processMicroservice(ctx context.Context) {
    // 获取请求ID
    if requestID, ok := ctx.Value(RequestID("request_id")).(string); ok {
        fmt.Println("Processing request:", requestID)
    }
    
    // 检查context是否已取消
    select {
    case <-ctx.Done():
        fmt.Println("Request cancelled")
        return
    default:
        // 继续处理
    }
}

func main() {
    // 创建带请求ID的context
    ctx := context.WithValue(context.Background(), RequestID("request_id"), "req-123")
    
    // 创建带超时的context
    ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
    defer cancel()
    
    // 处理请求
    processMicroservice(ctx)
}

Context最佳实践

使用Context时的一些重要注意事项和最佳实践。

package main

import (
    "context"
    "fmt"
    "time"
)

func main() {
    // 1. 总是传递context作为第一个参数
    func process(ctx context.Context, data string) {
        // 处理逻辑
    }
    
    // 2. 不要存储context在结构体中
    type Bad struct {
        ctx context.Context // 错误示例
    }
    
    // 3. 使用WithCancel、WithTimeout等派生context
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()
    
    // 4. 在goroutine中检查context取消
    go func() {
        for {
            select {
            case <-ctx.Done():
                return
            default:
                // 工作逻辑
            }
        }
    }()
    
    // 5. 使用context传递请求范围的值
    ctx = context.WithValue(ctx, "key", "value")
    
    // 6. 在HTTP处理函数中使用请求的context
    func handler(w http.ResponseWriter, r *http.Request) {
        ctx := r.Context()
        // 使用ctx
    }
}