Context 是用来控制一个请求的生命周期,以及周期内数据共享的一个 package
。
数据层面
依赖 valueCtx
,是一个嵌套实现的类 map
的数据结构
type valueCtx struct {
Context
key, val interface{}
}
通过 递归匹配,来读取最近的一次更新的值(考虑到一个请求周期内的数据应该不多,递归查询应该是可以接受的)
func (c *valueCtx) Value(key interface{}) interface{} {
if c.key == key {
return c.val
}
return c.Context.Value(key)
}
通过嵌套,来实现数据的写入。
func WithValue(parent Context, key, val interface{}) Context {
...
return &valueCtx{parent, key, val}
}
生命周期
Context
生命周期的管理,或者说是退出时通知机制的实现,主要是依赖 cancelCtx
.
type cancelCtx struct {
Context
mu sync.Mutex // protects following fields
done chan struct{} // created lazily, closed by first cancel call
children map[canceler]struct{} // set to nil by the first cancel call
err error // set to non-nil by the first cancel call
}
当 cancelCtx
第一次被从 Context
派生出来时,后台会通过 goroutine
的方式来同步 退出
事件。 通过 select
来检测 parent
的退出,然后退出
go func() {
select {
case <-parent.Done():
child.cancel(false, parent.Err())
case <-child.Done(): // block 机制,避免立即退出
}
}()
如果是从派生出来的对象中再次派生,则是通过 children
字段来维护派生关系. 当 parent
退出时,来主动退出所有的派生 Context
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
child.cancel(false, err)
}
cancel
的主要逻辑
if c.done == nil {
c.done = closedchan
} else {
close(c.done)
}
当 channel
被关闭的时候,其他所有在 select
监听的 routine
都会感知到,从而实现了通知机制。
WithDeadline
是在此基础上,增加了一个 timer
机制,通过后台来实现自己的主动退出。
type timerCtx struct {
cancelCtx
timer *time.Timer // Under cancelCtx.mu.
deadline time.Time
}
c.timer = time.AfterFunc(d, func() {
c.cancel(true, DeadlineExceeded)
})
主要逻辑差不多就是这些