Go-Context
介绍
在 Go 语言中,`context` 是一个用于在不同 Goroutine 之间传递请求范围数据、控制请求生命周期和处理取消信号的工具。它提供了一种机制来管理 Goroutine 之间的上下文信息,并允许在请求处理链中传递和取消上下文。
`context` 的主要优点和用途包括
1. 请求范围数据传递:`context` 允许在请求处理链中传递请求相关的数据,如请求 ID、用户身份验证信息等。这样可以避免在函数参数中传递大量的上下文信息。
2. 控制请求生命周期:`context` 允许设置超时时间、截止时间和取消信号,用于控制请求的处理时间和生命周期。当超时或取消信号触发时,相关的 Goroutine 可以及时退出,避免资源泄漏。
3. 处理取消信号:`context` 提供了一个取消机制,可以通过调用 `context` 的 `cancel` 方法来发送取消信号,通知相关的 Goroutine 停止处理请求。这对于处理长时间运行的请求或需要提前终止的情况非常有用。
应用场景
常见的 `context` 用法和应用场景包括
1.在 HTTP 服务器中,使用`context` 传递请求相关的数据和控制请求的生命周期,如超时控制、请求取消等。
2.在并发任务中,使用`context` 传递上下文信息和取消信号,以便在需要时中止任务的执行。- 在微服务架构中,使用 `context` 在服务之间传递请求范围的数据和控制请求的生命周期。
3.在测试中,使用`context` 控制测试的超时时间和取消信号,以确保测试的稳定性和可靠性。通过使用 `context`,可以更好地管理和控制 Goroutine 之间的上下文信息和请求在日志记录中,使用 context 传递请求相关的数据,如请求 ID、用户信息等,以便在日志中记录相关的上下文信息。
4.在数据库操作中,使用`context` 控制数据库查询的超时时间和取消信号,以避免长时间的阻塞和资源浪费。
5.在调用远程服务或 API 时,使用`context `控制请求的超时时间和取消信号,以确保及时处理超时或取消的情况。
6.在任务调度和并发控制中,使用 `context` 传递任务相关的数据和控制任务的生命周期,如任务取消、任务超时等。
Context
,中文叫做上下文
,Go语言在1.7
版本中新增的context
包中定义了Context
,Context
本质是一个接口,这个接口一共定义了四个方法:
type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key any) any }
使用方法
- 不要把Context放在结构体中,要以参数的方式传递
- 以Context作为参数的函数方法,应该把Context作为第一个参数,放在第一位
- 给一个函数方法传递Context时,不要传递nil,如果不知道传递什么,就使用context.TODO()
- Context的Value相关方法应该传递必须的参数,不要什么数据都使用这个传递
- Context是线程安全的,可以放心的在多个goroutine中传递
context.Background
ctx := context.Background()
context.TODO
context.TODO
:用于暂时不知道使用哪个 context
的情况。它也是一个空的 context
,但是建议尽快替换为合适的 context
ctx := context.TODO()
context.WithCancel
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context) { for { select { case <-ctx.Done(): // 被取消的时候触发执行 fmt.Println("worker stopped") err := ctx.Err() // 取消的原因 fmt.Printf("context is cancelled, reason: %v\n", err) return default: fmt.Println("worker working") time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) go worker(ctx) // 模拟一些工作 time.Sleep(2 * time.Second) cancel() // 取消 context // 给 worker 一些时间来处理取消信号 time.Sleep(1 * time.Second) } //worker working //worker working //worker working //worker stopped //context is cancelled, reason: context canceled
context.WithDeadline
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context) { for { select { case <-ctx.Done(): fmt.Println("worker stopped due to deadline or cancellation") return default: fmt.Println("worker working") time.Sleep(1 * time.Second) } } } func main() { deadline := time.Now().Add(2 * time.Second) ctx, cancel := context.WithDeadline(context.Background(), deadline) defer cancel() go worker(ctx) // 给 worker 一些时间来运行 time.Sleep(5 * time.Second) } //worker working //worker working //worker stopped due to deadline or cancellation
context.WithTimeout
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context) { for { select { case <-ctx.Done(): fmt.Println("worker stopped due to timeout or cancellation") return default: fmt.Println("worker working") time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() go worker(ctx) // 给 worker 一些时间来运行 time.Sleep(5 * time.Second) } //worker working //worker working //worker working //worker stopped due to timeout or cancellation
context.WithValue
package main import ( "context" "fmt" ) func processRequest(ctx context.Context) { value := ctx.Value("key") if val, ok := value.(string); ok { fmt.Printf("Value from context: %s\n", val) } } func main() { ctx := context.WithValue(context.Background(), "key", "Hello, World!") processRequest(ctx) } //Value from context: Hello, World!
流程图