上下文 context.Context Go 语言中用来设置截止日期、同步信号,传递请求相关值的结构体。上下文与 Goroutine 有比较密切的关系,是 Go 语言中独特的设计,在其他编程语言中我们很少见到类似的概念。
1. context使用
1.1. context接口
context.Context 是 Go 语言在 1.7 版本中引入标准库的接口1,该接口定义了四个需要实现的方法,其中包括:
-
Deadline — 返回 context.Context 被取消的时间,也就是完成工作的截止日期;
-
Done — 返回一个 Channel,这个 Channel 会在当前工作完成或者上下文被取消后关闭,多次调用 Done 方法会返回同一个 Channel;
-
Err — 返回 context.Context 结束的原因,它只会在 Done 方法对应的 Channel 关闭时返回非空的值;
-
- 如果 context.Context 被取消,会返回 Canceled 错误;
- 如果 context.Context 超时,会返回 DeadlineExceeded 错误;
-
Value — 从 context.Context 中获取键对应的值,对于同一个上下文来说,多次调用 Value 并传入相同的 Key 会返回相同的结果,该方法可以用来传递请求特定的数据;
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{
}
Err() error
Value(key interface{
}) interface{
}
}
Go
context 包中提供的 context.Background、context.TODO、context.WithDeadline 和 context.WithValue 函数会返回实现该接口的私有结构体,我们会在后面详细介绍它们的工作原理。
1.2. context初始化方法
在Go语言中,context包提供了管理并发操作的上下文(Context)。以下是初始化context的几种常见方法:
1.2.1. context.Background()
- 创建一个空的context,通常用于顶层函数。
ctx := context.Background()
1.2.2. context.TODO():
context.TODO()函数也会生成一个空的Context,与context.Background()相似,返回的Context也没有任何附加信息。但是,context.TODO()的用途在于标记那些还不确定应该使用什么Context的地方。它主要用于代码开发阶段,作为一个临时的占位符,表示开发者计划在未来确定并替换为更具体的Context。使用context.TODO()可以提醒开发者和维护者,这里的Context是需要进一步审查和确定的
ctx := context.TODO()
1.2.3. context.WithCancel(parent Context):
- 创建一个可以被取消的context,当调用返回的cancel函数时,将会触发此context及其子context的取消。
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
1.2.4. context.WithDeadline(parent Context, deadline time.Time)
- 创建一个将在指定时间deadline触发取消的context。
d := time.Now().Add(50 * time.Millisecond)
ctx, cancel := context.WithDeadline(context.Background(), d)
defer cancel()
1.2.5. context.WithTimeout(parent Context, timeout time.Duration)
- 创建一个在指定的超时时间后将触发取消的context。
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel()
1.2.6. context.WithValue(parent Context, key interface{}, val interface{})
- 创建一个可以附加键值对的context。
ctx := context.WithValue(context.Background(), "key", "value")
这些方法可以组合使用,以创建具有特定行为和功能的context
2. context原理
2.1. 设计原理 #
在 Goroutine 构成的树形结构中对信号进行同步以减少计算资源的浪费是 context.Context 的最大作用。Go 服务的每一个请求都是通过单独的 Goroutine 处理的2,HTTP/RPC 请求的处理器会启动新的 Goroutine 访问数据库和其他服务。
如下图所示,我们可能会创建多个 Goroutine 来处理一次请求,而 context.Context 的作用是在不同 Goroutine 之间同步请求特定数据、取消信号以及处理请求的截止日期。
图 6-1 Context 与 Goroutine 树
每一个 context.Context 都会从最顶层的 Goroutine 一层一层传递到最下层。context.Context 可以在上层 Goroutine 执行出现错误时,将信号及时同步给下层。