在日常开发中,我们通常会在函数的第一个参数设置为 ctx context.context,这就是golang 的 Context包,目的是用来传递上下文信息。
context都能干啥
说起go大家第一反应会想到协程,协程也具有父子关系,主协程与子协程之间存在控制、协同、取消的需求,如:
- 主协程和子协程之间的通信问题,如主协程取消后通知子协程进行取消
- 如何对上下文信息进行传递,如请求头里的用户信息,设备信息等
- 控制下游rpc接口的超时时间,因为请求基于协程,下游响应慢时,会导致协程数激增,内存上涨,严重可导致服务不可用。
context便是为解决上述问题而生,它可以传递取消信息、超时时间以及携带kv信息等。
如何创建context
- context.Background() 和context.TODO() 创建最基本的context
- context.WithCancel 传入父context,返回新的context和用于取消该新context的函数
- context.WithTimeout和context.WithDeadline,也是传入父context,返回新的context和用于取消该新context的函数,内部会设置定时器,到达时间后自动触发cancelFunc
- context.WithValue:传入父context一对kv,返回一个新的Context,新的Context内部会记录这对kv,供以后查询
func main() {
root := context.Background() //初始化
ctx1 := context.WithValue(root, "k1", 1111) //携带kv的新context
ctx2, cancel1 := context.WithCancel(ctx1) //带取消信号的context
defer cancel1()
ctx3, cancel2 := context.WithTimeout(ctx2, 2*time.Second) //带超时信号
defer cancel2()
ctx4 := context.WithValue(ctx2, "k2", "2222")
ctx5 := context.WithValue(ctx4, "k3", "3333")
ctx6 := context.WithValue(ctx3, "k4", "4444")
ctx7, cancel3 := context.WithDeadline(ctx4, time.Now().Add(4*time.Second))
defer cancel3()
ctx8, cancel4 := context.WithCancel(ctx6)
defer cancel4()
fmt.Println(ctx7.Value("k1"))
fmt.Println(ctx6.Value("k4"))
fmt.Println(ctx5.Value("k2"))
fmt.Println(ctx7)
go func() {
select {
case <-ctx7.Done():
fmt.Println("ctx7 canceled")
}
}()
go func() {
select {
// ctx8会在7之前cancel,因为ctx3被cancel了
case <-ctx8.Done():
fmt.Println("ctx8 canceled")
}
}()
time.Sleep(5 * time.Second)
}
//输出
1111
4444
2222
context.Background.WithValue(type string, val <not Stringer>).WithCancel.WithValue(type string, val 2222).WithDeadline(2022-04-17 21:54:16.807868 +0800 CST m=+4.000185716 [3.999804083s])
ctx8 canceled
ctx7 canceled
context有哪些接口
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
- Deadline:获取是否设置了到期时间以及所设置的截止时间。如果有设置过期时间的话,Context会到那个时间点时自动发起取消Context的操作。
- Done:返回一个通道,如果通道关闭则代表该Context已经被取消;如果返回的为nil,则代表该Context是一个永远不会被取消的Context。
- Err:返回该Context被取消的原因,如果只使用Context包的Context类型的话,那么只可能返回Canceled(代表被明确取消)或者DeadlineExceeded(因超时而取消)
- Value:你可能经常看到代码中使用该函数从ctx中获取一个string key中的值,即一个Context内部是可以携带很多kv的, 关于如何存储,可以想象成一个树中的某个节点,每个节点保存了指向父节点的指针,从当前的节点一层层向根节点寻找
网友评论