• 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

相关文章: