Go语言中的context包:掌握上下文管理

发表时间: 2024-05-24 16:37

context 上下文管理

context 翻译过来就是上下文管理, 主要作用有两个:

控制 goroutine 的超时

保存上下文数据

1 使用

1.1 核心接口Context

type Context interface {// Deadline returns the time when work done on behalf of this context// should be canceled. Deadline returns ok==false when no deadline is// set.Deadline() (deadline time.Time, ok bool)// Done returns a channel that's closed when work done on behalf of this// context should be canceled.Done() <-chan struct{}// Err returns a non-nil error value after Done is closed.Err() error// Value returns the value associated with this context for key.Value(key interface{}) interface{}}

简单介绍一下其中的方法:

- Done会返回一个channel, 当该context被取消的时候, 该channel会被关闭, 同时对应的使用该context的routine也应该结束并返回。

- Context中的方法是协程安全的, 这也就代表了在父routine中创建的context, 可以传递给任意数量的routine并让他们同时访问。

- Deadline会返回一个超时时间, routine获得了超时时间后, 可以对某些io操作设定超时时间。

- Value可以让routine共享一些数据, 当然获得数据是协程安全的。

在请求处理的过程中, 会调用各层的函数, 每层的函数会创建自己的routine, 是一个routine树。所以, context也应该反映并实现成一棵树。

要创建context树, 第一步是要有一个根结点。context.Background函数的返回值是一个空的context, 经常作为树的根结点, 它一般由接收请求的第一个routine创建, 不能被取消、没有值、也没有过期时间。

func Background() Context

之后该怎么创建其它的子孙节点呢? 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 interface{}, val interface{}) Context

这四个函数的第一个参数都是父context, 返回一个Context类型的值, 这样就层层创建出不同的节点。

子节点是从复制父节点得到的, 并且根据接收的函数参数保存子节点的一些状态值, 然后就可以将它传递给下层的routine了。

WithCancel函数, 返回一个额外的CancelFunc函数类型变量, 该函数类型的定义为:

type CancelFunc func()

调用CancelFunc对象将撤销对应的Context对象, 这样父结点的所在的环境中, 获得了撤销子节点context的权利, 当触发某些条件时, 可以调用CancelFunc对象来终止子结点树的所有routine。

在子节点的routine中, 需要用类似下面的代码来判断何时退出routine:

select {case <-cxt.Done():// do some cleaning and return}

根据cxt.Done()判断是否结束。当顶层的Request请求处理结束, 或者外部取消了这次请求, 就可以cancel掉顶层context, 从而使整个请求的routine树得以退出。

WithDeadline和WithTimeout比WithCancel多了一个时间参数, 它指示context存活的最长时间。如果超过了过期时间, 会自动撤销它的子context。所以context的生命期是由父context的routine和deadline共同决定的。

WithValue返回parent的一个副本, 该副本保存了传入的key/value, 而调用Context接口的Value(key)方法就可以得到val。注意在同一个context中设置key/value,若key相同, 值会被覆盖。