阅读量:0
作用
context字面意思上下文,用于关联管理上下文,具体有如下几个作用
- 取消信号传递:可以用来传递取消信号,让一个正在执行的函数知道它应该提前终止。
- 超时控制:可以设定一个超时时间,自动取消超过执行时间的操作。
- 截止时间:与超时类似,但是是设定一个绝对时间点,而不是时间段。
- 值传递:可以安全地在请求的上下文中传递数据,避免了使用全局变量或者参数列表不断增长。
由上述看出,context有个重要用途,控制取消。
场景与用法
示例1:HTTP 请求处理
在处理 HTTP 请求时,可以为每个请求创建一个 context,用于控制请求处理的整个生命周期。如果请求被取消或超时,依赖该 context 的操作也会被取消。
代码示例
package main import ( "context" "fmt" "math/rand" "net/http" "time" ) func randomSleepAtMost2s() { rand.Seed(time.Now().UnixNano()) // 初始化随机数种子 // 生成 0 到 2000 之间的随机整数(毫秒) randomMillis := rand.Intn(2000) // 转换为 time.Duration 类型,并乘以 time.Millisecond sleepDuration := time.Duration(randomMillis) * time.Millisecond // 随机睡眠 fmt.Println("sleeping for", sleepDuration) time.Sleep(sleepDuration) } func handler(w http.ResponseWriter, r *http.Request) { // 超时时间1s ctx, cancel := context.WithTimeout(r.Context(), 1*time.Second) defer cancel() // 创建一个模拟正常处理完成的通道 done := make(chan struct{}) // 模拟异步处理逻辑 go func() { // 模拟耗时操作 // - 当随机睡眠超过1s时,会触发 ctx.Done(),取消请求 // - 当随机睡眠不超过1s时,则会正常处理请求 randomSleepAtMost2s() fmt.Println("request processed") close(done) // 处理完成,关闭通道 }() // 模拟耗时操作 select { case <-done: w.WriteHeader(http.StatusOK) w.Write([]byte("request processed successfully")) case <-ctx.Done(): fmt.Println("request cancelled") http.Error(w, "request cancelled", http.StatusRequestTimeout) return } } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) }
示例2 :数据库操作
数据库查询或操作可以接受一个 context 参数,允许查询在超时或被取消时立即停止,避免无谓的数据库资源占用。
代码示例
package main import ( "context" "database/sql" "fmt" "time" _ "github.com/lib/pq" // 假设使用 PostgreSQL ) func queryWithTimeout(ctx context.Context, db *sql.DB, query string) (*sql.Rows, error) { // 设置1s的超时时间 ctx, cancel := context.WithTimeout(ctx, 1*time.Second) defer cancel() return db.QueryContext(ctx, query) } func main() { // 连接数据库(示例) db, err := sql.Open("postgres", "your_connection_string") if err != nil { panic(err) } defer db.Close() ctx := context.Background() _, err = queryWithTimeout(ctx, db, "SELECT * FROM your_table") if err != nil { fmt.Println("Query failed:", err) } }
示例3:取消协程
在启动多个 goroutine 进行并发操作时,可以通过 context 控制这些 goroutine 的生命周期,确保它们能够在必要时被正确取消。
代码示例
package main import ( "context" "fmt" "time" ) func operation(ctx context.Context, id int) { select { case <-time.After(2 * time.Second): fmt.Printf("Operation %d completed\n", id) case <-ctx.Done(): fmt.Printf("Operation %d cancelled\n", id) } } func main() { ctx, cancel := context.WithCancel(context.Background()) for i := 0; i < 5; i++ { go operation(ctx, i) } time.Sleep(1 * time.Second) cancel() // 取消所有协程操作 time.Sleep(3 * time.Second) // 等待足够的时间以打印完日志,观察效果 }
示例4:跨服务调用
在微服务架构中,一个服务调用另一个服务时,可以通过 context 传递关于原始请求的信息,如请求ID,以便进行链路追踪。
代码示例
package main import ( "context" "fmt" "net/http" "time" ) func callService(ctx context.Context, url string) { req, _ := http.NewRequest("GET", url, nil) req = req.WithContext(ctx) client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Println("Request failed:", err) return } defer resp.Body.Close() fmt.Println("Response status:", resp.Status) } func main() { ctx := context.Background() ctx = context.WithValue(ctx, "RequestID", "abc123") ctx, cancel := context.WithTimeout(ctx, 1*time.Second) defer cancel() callService(ctx, "http://example.com") }
结语
通过这些场景和用法,可以看出 context
在 Go 中的重要性,特别是在需要控制管理请求生命周期时(控制取消)。