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
}
}