Go微服务实战
上QQ阅读APP看书,第一时间看更新

8.3.2 定义

context包的核心是Context接口,其定义如下:


type Context interface{
    Deadline() (deadline time.Time,ok bool)
    Done() <- chan struct{}
    Err() error
    Value(key interface{}) interface{}
}

Context接口内定义了四个方法,分别如下。

▪Deadline方法:需要返回当前Context被取消的时间,也就是截止时间。

▪Done方法:需要返回一个channel,该channel会在工作完成或者Context被取消时关闭,多次调用Done方法返回的是同一个channel。

▪Err方法:用于返回当前Context结束的原因,仅在Done方法返回的channel被关闭时才返回非空值,这里包含两种错误,如果当前Context被取消,则返回Canceled错误;如果当前Context超时,则返回DeadlineExceeded错误。

▪Value方法:用来取得当前Context上绑定的值,是一个键值对,所以参数是一个key值,多次调用该方法而参数相同的话,返回的结果也相同。

虽然Context是一个接口,但是标准包里面实现了其他的两个方法:Background方法和TODO方法,可通过这两个方法来使用Context。在介绍这两个方法之前,需要先介绍一下Context的实现。

Context在数据结构上是一种单向继承关系,最开始的Context起到类似于初始化的作用,里面有一些数据,下一层的Context会继承上一层的Context,新的Context可以有children,children就是在上一层的Context外面再套一层,新扩的一层可以存储与自己相关的数据。这种多层结构可以像启动goroutine一样扩展很多层。

理解了Context的分层模式,就可以方便地理解Background和TODO方法了,这两个方法用于返回私有化的变量background和todo,这两个变量就存储于最顶层的parent Context中,后续的Context都是衍生自这个parent,形成树状层次。当一个parent Context被取消时,继承自它的所有Context都会被取消。

下面来看一下这两个方法在源码中的实现:


func Background() Context {
    return background
}

func TODO() Context {
    return todo
}

background和todo两个私有变量是在context包初始化的时候就定义好的,Background和TODO这两个方法也没有什么差别,可以理解为二者互为别名,只是Background方法是每个Context的顶层默认值,用于main函数,以及初始化、测试等代码中,它作为根Context是不可以被取消的。而TODO方法则是在不确定的时候使用的,但现实中很少使用。

background和todo这两个私有变量其实是两个指针,指向emptyCtx结构体实例。emptyCtx的定义如下:


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
}

可以看到,本质上background和todo是不携带任何信息的Context,不可取消,没有截止时间;而衍生出来的Context都继承自这个根Context。