golang 中的 context
背景
在 Go server 中,每个刚收到的 request 都由对应的 goroutine 处理。Requests handlers 经常开启一个额外的 goroutines 去访问后端,比如数据库或者 RPC 服务。
工作在特定 Request 上的 goroutine 集合通常需要访问 Request 指定的数据,比如终端用户的身份,认证秘钥,以及最后期限。
当一个 Request 取消后或者 time out(过期), 服务于这个 Request 的 goroutine 都需要迅速退出,这样系统可以回收他们正在使用的资源
go 中开发了 context 包来方便地在处理同一个 Request, 跨越 API 边界的 goroutine 的集合中传递 数据,取消信号和最后期限。
Context
Context 接口定义
1 | DeadLine() (deadline time.Time, ok book) |
Deadline
1 | `Deadlin()(deadline time.Time, ok bool` |
返回当前任务应当结束的时间,同样也是这个 context 应当取消的时间
ok == false 表示没有设置 deadline. 对 Deadline 的连续的成功的调用返回相同的值
Done
1 | `Done() <- chan struct{}` |
返回一个 closed 的 channel. 当任务已经结束,表示当前的 context 的应当被结束。Done 可能返回 nil 表示当前 context 没有永远不能取消。对 Done 成功的调用应当返回同样的值。Done 用语 select 语句。WithCancel 当 cancel 发生时将 Done 为 closedWithDeadline 当 deadline 到了后将 Done 设置为 closedWithTimeout 当 timeout 时间计数过期后将 Done 设置为 closed
1 | func Stream(ctx context.Context, out chan <- Value) error { |
Err
1 | `Err() error` |
如果 Done 尚未设置为 closed, Err 将返回 nil
如果 Done 已经 closed, Err 将返回 Non-nil error 说明原因Canceled 表示 context 已经 canceledDeadlineExceeded 表示 context 的 deadline 已经过期
当 Err 返回 non-nil 错误后,对 Err 成功的调用都应返回同样的值
Value
1 | Value(Key interface{}) interface{} |
该接口返回在的 context 与 key 相关联的 value, 当没有对应的 key 则返回 value
对 Value 成功的调用相同的 key 下应返回相同的 value
应仅在跨进程和 API 边界传递 request scope 的数据时使用 context value.
1 | package user |
Derived contexts
context 包支持从已有的 context 中创建新的 context. 这些继承关系为一个树结构,且当父 context 状态变为 canceled 时,继承自该 context 的 context 也会变为 canceled.
所有 Context 树的根节点均为 Background, 且状态永远不会变为 canceled
1 | func Background() Context |
WithCancel 和 WithTimeout
WithCancel 和 WithTimeout 返回继承自参数的 context, 他会比父 context 更早的变为 canceled.
与到来的 Request 相关联的 context 通常会在 request handler 结束时变为 canceled.WithCancel 通常用于当使用多个 replicas 时终止多余的 request.WithCancel 通常用于对后端的 request 设置 deadline
1 | func WithCancel(parent Context)(ctx Context, cancel CancelFunc) |
WithValue
WithValue 提供了一个将 request-scopeed 数据与 context 关联的方式
1 | func WithValue(parent Context, key interface{}, value interface{}) Context |