- golang 的 Context 包,是专门用来简化对于处理单次请求但是涉及到多个 goroutine 之间与请求域的数据、取消信号、截止时间等相关操作,这些操作可能涉及多个 API 调用
- 常见场景如一个微服务后台,各个 RPC 接口逐个调用形成一个调用链,某一时刻超时,要通知调用链上所有正在调用的请求断开连接
- 这样的话, 我们就可以通过 Context,来跟踪这些 goroutine,并且通过 Context 来控制他们的目的,这就是 Golang 语言为我们提供的 Context,中文可以称之为”上下文“
1. 定义
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
-
Context 的主要数据结构是一单向的继承关系的结构
- 根据使用场景的不同,每一层context都具备有一些不同的特性
- 这种层级式的组织也使得 context 易于扩展,职责清晰
-
Deadline 方法是获取设置的截止时间的意思
- 第一个返回值为截止时间,即到了这个时间点,Context 会自动发起取消请求
- 第二个返回值为 false 时表示没有设置截止时间,如果需要取消的话,需要调用取消函数进行取消
-
Done 方法返回一个只读的chan,类型为 struct{}
- 在 goroutine 中,如果该方法返回的chan可以读取,则意味着 parent context 已经发起了取消请求
- 我们通过 Done 方法收到这个信号后,就应该做清理操作,然后退出 goroutine,释放资源
- 之后,Err 方法会返回一个错误,告知为什么 Context 被取消
-
Err 方法返回取消的错误原因,因为什么 Context 被取
-
Value 方法获取该 Context 上绑定的值,是一个键值对,所以要通过一个 Key 才可以获取对应的值,这个值一般是线程安全的
2. 基本实现
- Golang 内置的 context 包,已经给出了 Context 的两个实现
- 一般在代码中,开始上下文的时候都是以这两个作为最顶层的 parent context 节点,然后再衍生出子 context
- 这些 Context 对象形成一棵树:当一个 Context 对象被取消时,它的所有子节点都会被取消。两个实现如下:
![]()
type emptyCtx int
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
func (*emptyCtx) Err() error {
return nil
}
func (*emptyCtx) Value(key interface{}) interface{} {
return nil
}
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
func Background() Context {
return background
}
func TODO() Context {
return todo
}
View Code