Context是什么
首先Context是一个request的上下文,用于做上下文生命周期管理
的。可控制gorutine的超时、取消,做信号传递;也可在生命周期内,做全局的变量传递功能
(比如请求的用户信息、追踪信息)。
Context接口的核心方法
- Deadline() (deadline time.Time, ok bool) 返回截止时间,是否存在截止时间
- Done() <-chan struct 用于传递上下文取消信号的只读channel
- Err() error 上下文的报错信息
- Value(key any) any 提取上下文保存的全局变量(可以用context.WithValue塞入值,用Value提取)
Ctx怎么创建的?
context.Background()
创建一个没有取消、超时的空Context,一般作为根ctx使用
nctx := ctx.Background()
context.TODO()
创建一个类似ctx.Background()的上下文,但一般作为ctx的临时变量来使用,比如:
// 假设这里有一个函数,暂时不确定外部会传入什么样的Context
funcDoSomething := func(ctx context.Context) {
// 这里先使用TODO作为占位符
var todoCtx context.Context = context.TODO()
// 假设根据某个条件来决定是否设置超时
if someCondition() {
// 如果条件满足,设置2秒超时
todoCtx, _ = context.WithTimeout(ctx, 2*time.Second)
} else {
// 否则使用传入的Context
todoCtx = ctx
}
// 使用处理后的Context执行其他操作
//...
}
context.WithValue(parent Context, key, val any)
往ctx里塞入参数,返回一个包含参数的孩子ctx
// 塞入参数
queryParamKey := "query_param_key"
param := &Param{}
ctx = context.WithValue(ctx, setValueKey, param)
// 提取参数
val := ctx.Value(queryParamKey).(*Param)
context.WithCancel()
context.WithCancel(ctx context) (ctx Context, cancel CancelFunc) 输入父母ctx,返回孩子ctx, 当调用cancel func时,会取消孩子ctx和派生的所有ctx,并向孩子ctx.Done()发送取消信号
ctx, cancelFunc := context.WithCancel(context.Background())
cancelFunc() //调用取消
context.WithCancelCause()
ctx, cancelFunc := context.WithCancelCause(context.Background())
err := errors.New("new error")
cancelFunc(err)
context.Cause() error
返回取消ctx的原因,返回一个error
err := context.Cause()
context.WithDeadline() 和 context.WithTimeout()
都是接收一个根ctx和时间,用于做ctx的时间控制,只是时间的控制方式不一样
第一个WithDeadline,指定时间点,在指定时间超时
第二个WithTimeout,指定时间段,在过了时间段后超时
使用场景
传递全局变量
示例如上文,已贴过
// 塞入参数
queryParamKey := "query_param_key"
param := &Param{}
ctx = context.WithValue(ctx, setValueKey, param)
// 提取参数
val := ctx.Value(queryParamKey).(*Param)
控制超时
timeOutCtx, _ = context.WithTimeout(ctx, 2*time.Second)
go func(){
doSomething(timeOutCtx)
}
任务取消
比如结合超时控制,做超时取消后的任务补偿
// doJobA 模拟一个可能会执行较长时间的任务
func doJobA(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("doJobA: 任务被取消,原因:", ctx.Err())
return
case <-time.After(3 * time.Second):
fmt.Println("doJobA: 任务完成")
}
}
// doJobB 模拟超时取消后的补偿任务
func doJobB() {
fmt.Println("doJobB: 执行补偿任务")
}
func main() {
// 创建一个基础上下文
ctx := context.Background()
// 创建一个带有超时的上下文
timeOutCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
// 启动一个 goroutine 执行任务 doJobA
go func() {
defer cancel()
doJobA(timeOutCtx)
}()
// 等待超时信号
<-timeOutCtx.Done()
// 检查上下文取消的原因
if err := timeOutCtx.Err(); err == context.DeadlineExceeded {
fmt.Println("超时触发,原因:", err)
// 执行补偿任务
doJobB()
} else {
fmt.Println("任务正常结束,未超时,无需补偿")
}
fmt.Println("主程序结束")
}