在 Go 服务中,每个传入的请求在单独的 goroutine
中处理。请求回调函数通常启动额外的 goroutine
以访问后端,如数据库和RPC服务。处理同一请求的一系列 goroutine
通常需要访问请求相关的值,例如端用户的标识、授权令牌和请求截止时间。当请求被取消或超时,处理该请求的所有 goroutine
都应该快速退出,以便系统可以回收它们正在使用的资源。
在 Google,我们开发了一个上下文包,可以轻松地跨越 API边界,将请求作用域内的值、取消信号和截止时间传递给所有处理请求的 goroutine
。该包的公共可用版本为 context
。本文描述了如何使用这个包,并提供了一个完整的示例。
Context
context 包的核心是 Context
类型:
// A Context carries a deadline, cancelation signal, and request-scoped values // across API boundaries. Its methods are safe for simultaneous use by multiple // goroutines. type Context interface { // Done returns a channel that is closed when this Context is canceled // or times out. Done() <-chan struct{} // Err indicates why this context was canceled, after the Done channel // is closed. Err() error // Deadline returns the time when this Context will be canceled, if any. Deadline() (deadline time.Time, ok bool) // Value returns the value associated with key or nil if none. Value(key interface{}) interface{} }
(此描述是精简的;godoc 是权威的。)
Done
方法返回一个 channel,充当传递给 Context
下运行的函数的取消信号:当 channel 关闭时,函数应该放弃它们的工作并返回。Err
方法返回一个错误,表明取消 context
的原因。文章 Pipelines and Cancelation 更详细地讨论了 Done
channel 的习惯用法。
Context
没有 Cancel
方法,原因与 Done
channel 是只读的一样:接收取消信号的函数通常不是发送信号的函数。特别是当父操作为子操作启动 goroutine
时,子操作不应该有能力取消父操作。相反,WithCancel
函数(如下所述)提供了一种取消新 Context
值的方法。
多个 goroutine
同时使用同一 Context
是安全的。代码可以将单个 Context
传递给任意数量的 goroutine
,并取消该 Context
以向所有 goroutine
发送信号。
Deadline
方法允许函数决定是否应该开始工作;如果剩下的时间太少,则可能不值得。代码还可以使用截止时间来设置I/O操作超时。
Value
允许 Context
携带请求作用域的数据。为使多个 goroutine
同时使用,这些数据必须是安全的。
Derived contexts
context
包提供了从现有 Context
值派生新 Context
值的函数。这些值形成一个树:当 Context
被取消时,从它派生的所有 Context
也被取消。
Background
是所有 Context
树的根;它永远不会被取消:
// Background returns an empty Context. It is never canceled, has no deadline, // and has no values. Background is typically used in main, init, and tests, // and as the top-level Context for incoming requests. func Background() Context
WithCancel
和 WithTimeout
返回派生 Context
值,可以比父 Context
更早取消。当请求回调函数返回时,通常会取消与传入请求关联的 Context
。WithCancel
还可用于使用多个副本时取消冗余的请求。WithTimeout
用于设置对后端服务器请求的截止时间:
// WithCancel returns a copy of parent whose Done channel is closed as soon as // parent.Done is closed or cancel is called. func WithCancel(parent Context) (ctx Context, cancel CancelFunc) // A CancelFunc cancels a Context. type CancelFunc func() // WithTimeout returns a copy of parent whose Done channel is closed as soon as // parent.Done is closed, cancel is called, or timeout elapses. The new // Context's Deadline is the sooner of now+timeout and the parent's deadline, if // any. If the timer is still running, the cancel function releases its // resources. func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
WithValue
提供了一种将请求作用域的值与 Context
关联的方法:
// WithValue returns a copy of parent whose Value method returns val for key. func WithValue(parent Context, key interface{}, val interface{}) Context
通过一个示例了解使用 context
包的最佳方法。
示例:Google Web搜索
我们的示例是一个 HTTP 服务器,它处理 URL,如 _/search?q=golang&timeout=1s_,将查询 “golang” 转发到 google web search api 并渲染结果。timeout 参数告诉服务器在该延时后取消请求。
代码分为三个包: