什么是Context
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
我以前对Context的理解,就是从字面上理解:上下文,一个请求链路中一直存在的某信息。打个比方,Client请求A-Service到B-Service,B-Service再到C-Service,在这个请求链路中,上游就会将内容传递给下游,A-->B,B--->C是保持一个请求过程的。Context是程序单元的一个运行状态、现场、快照。结合这句话,我对Context的理解是Context用于保证一个Request在同一个生命周期内。
在Go语言中,程序单元指的就是Goroutine。
所以,一个Request,可能会在多个goroutine中去处理,多个goroutine可能共享Request信息,都在同一个生命周期内,如果Request请求取消或超时,则所有的goroutine都应该结束。
为什么需要Context
上述刚才说一个请求可能会在多个goroutine中去处理,所以如果其中一个goroutine超时了或者中断了,那这个Request就应该被立即停止结束,而不是一直等待。
所以,每个长请求都应该有个超时限制,一个长请求需要一个Context来保证整个请求都是在同一个生命周期内。有点“一荣俱荣,一损俱损”的味道。
使用场景
任何可能被阻塞,或者需要很长时间来完成的,都应该有个 context.Context
- rpc调用
- 长请求,长链路/多函数调用
Go中Context用法
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
Goroutine的创建和调用关系是分层级的。 为了实现这种关系,Context结构像一棵树,叶子节点须总是由根节点衍生出来的。
创建Context
- context.Background() 一般使用此方法
- context.TODO() 在目前还不清楚要使用的上下文时,或上下文尚不可用时,使用此方法
创建子节点
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{}) Contex
使用Context原则
- 不要把Context存在一个结构体当中,要显式地传入函数。Context变量需要作为第一个参数使用,一般命名为ctx;
- 即使方法允许,也不要传入一个nil的Context。如果你不确定要用什么Context,那么传一个context.TODO
- 使用context的Value方法时,只应该在程序和接口中传递“和请求相关的元数据”,不要用它来传递一些可选的参数;
- 要养成关闭 Context 的习惯,在建立之后,立即 defer cancel() 是一个好习惯
Q&A
1、如果不使用Context来保证请求处于一个生命周期,是否有其他的方式?
2、对于Context源码分析--->待办
参考资料: