context 包是干嘛的? 这是我刚开始了解时最大的疑惑。
简单来说就是上下文,用来追踪信息的一棵树,我们可以对这棵树的节点进行删除和增加,在这个节点上保存数据
应用场景:
在 Go http
包的 Server
中,每一个请求在都有一个对应的 goroutine 去处理。请求处理函数通常会启动额外的 goroutine 用来访问后端服务,比如数据库和 RPC 服务。用来处理一个请求的 goroutine 通常需要访问一些与请求特定的数据,比如终端用户的身份认证信息、验证相关的 token、请求的截止时间。当一个请求被取消或超时时,所有用来处理该请求的 goroutine 都应该迅速退出,然后系统才能释放这些 goroutine 占用的资源
context 原理
context 的调用时链式的通过 context 包中的几个函数来进行派生或取消 WithCancel
WithDeadline
WithTimeout
WithValue
父 contenxt(父节点)取消时其派生的子节点都将取消
通过 context.WithXXX
都将返回新的 Context
和 CancelFunc
(这是取消的一个回调)。调用 CancelFunc 将取消子代,移除父代对子代的引用,并且停止所有定时器。未能调用 CancelFunc
将泄漏子代,直到父代被取消或定时器触发。go vet
工具检查所有流程控制路径上使用 CancelFuncs
。
context 包接口如下
type Context interface {
// 当Context 被 canceled 或是 times out 的时候,Done 返回一个被 closed 的channel
Done() <-chan struct{}
// 在 Done 的 channel被closed 后, Err 代表被关闭的原因
Err() error
// 如果存在,Deadline 返回Context将要关闭的时间
Deadline() (deadline time.Time, ok bool)
// 如果存在,Value 返回与 key 相关了的值,不存在返回 nil
Value(key interface{}) interface{}
}
所有方法
func Background() Context
func TODO() Context
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context
上面可以看到 Context
是一个接口,想要使用就得实现其方法。在 context
包内部已经为我们实现好了两个空的 Context,可以通过调用 Background()
和 TODO()
方法获取。一般的将它们作为 Context
的根,往下派生
WithCancel
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := newCancelCtx(parent)
propagateCancel(parent, &c)
return &c, func() { c.cancel(true, Canceled) }
}
// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) cancelCtx {
return cancelCtx{
Context: parent,
done: make(chan struct{}),
}
}
以一个新的 Done channel
返回一个父 Context
的拷贝
此示例(来自官方包 demo)演示使用一个可取消的上下文,以防止 goroutine
泄漏。示例函数结束时,defer
调用 cancel
方法,gen goroutine
将返回而不泄漏。
WithDeadLine
简单来说就是在在设定的时间内执行通道关闭操作。
情况 1、上下文父节点关闭早于当前节点关闭,此时当前节点也关闭。
情况 2、在指定时间进行关闭函数操作。
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
// The current deadline is already sooner than the new one.
return WithCancel(parent)
}
c := &timerCtx{
cancelCtx: newCancelCtx(parent),
deadline: deadline,
}
官方 demo:
WithTimeout
顾名思义是用来是超时处理的,WithTimeout
返回 WithDeadline(parent, time.Now().Add(timeout))
。
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return WithDeadline(parent, time.Now().Add(timeout))
}
官方 demo:
WithValue
func WithValue(parent Context, key, val interface{}) Context {
if key == nil {
panic("nil key")
}
if !reflect.TypeOf(key).Comparable() {
panic("key is not comparable")
}
return &valueCtx{parent, key, val}
}
简单来说就是用来存 key-value 的 map, 并且当前节点及其后代的节点均可以拿到此 map 中的值
WithValue 返回的父与键关联的值在 val 的副本。
使用上下文值仅为过渡进程和 Api 的请求范围的数据,而不是将可选参数传递给函数。
提供的键必须是可比性和应该不是字符串类型或任何其他内置的类型以避免包使用的上下文之间的碰撞。WithValue 用户应该定义自己的键的类型。为了避免分配分配给接口 {} 时,上下文键经常有具体类型结构 {}。另外,导出的上下文关键变量静态类型应该是一个指针或接口。
官方 demo:
参考 Deepzz 的博客
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于