一个常见的场景:有一个接口请求需要较长的时间(如5秒),那么,用户很可能等不及,直接就放弃了请求。
而这个接口的任务,如果在用户放弃请求后依然继续执行,那么就是浪费服务器的资源了。
所以,我们需要在得知请求中断后,主动结束耗时的任务。
context
然而这个问题的核心是如何得知http的请求是否中断。这个问题在网上居然没什么资料。。。
示例演示
taskFunc1taskFunc2
// 该方法模拟进行任务,需要1秒钟
func taskFunc1(ctx context.Context, end chan string) {
// 监听任务执行情况的协程
result := make(chan string)
// 启动协程执行任务
go func() {
time.Sleep(1 * time.Second)
result <- "success" // 5秒后完成任务,将结果写入信道
}()
// 监听 context的结束信号和任务结束的信号,哪个先到就先执行哪个
select {
case <-ctx.Done():
end <- "taskFunc1失败"
case <-result:
end <- "taskFunc1成功"
}
}
// 该方法模拟进行任务,需要5秒钟,和上面是一样的
func taskFunc2(ctx context.Context, end chan string) {
result := make(chan string)
go func() {
time.Sleep(5 * time.Second)
result <- "success"
}()
select {
case <-ctx.Done():
end <- "taskFunc2失败"
case <-result:
end <- "taskFunc2成功"
}
}
他们内容都是一样的,都有两个参数:
ctx context.Contexthttp请求上文任务下文end chan string
接下来实现一个接口:
// 1.一个测试接口
func Test1(c *gin.Context) {
ctx := c.Request.Context() // 获取http请求的上下文对象
// 主线程判断两个协程是否运行结束的信道
end1 := make(chan string)
end2 := make(chan string)
// 启动两个协程分别完成任务
go taskFunc1(ctx, end1)
go taskFunc2(ctx, end2)
// 监听两个协程是否结束
err2, err1 := <-end2, <-end1
fmt.Println(err1, err2)
}
taskFunc2
taskFunc2taskFunc2
gin.Context
gin.ContextContext
context
gin.Context.Request.Context()
适用场景
contextgin.Context
mongo-drivergo-rediscontext.Context
context.TODO()context
context
于是现在也就知道了mongodb和redis要求传入这个参数的意义了:就是为了让我们能够主动结束它的任务,而不至于浪费资源继续请求数据,毕竟数据库资源是宝贵的。
当然,我们有必要每次都将gin的Context传入这些api吗?其实没有必要,因为大多数对数据库资源的请求也就是那么几十毫秒,为了节省这点资源,我们的代码需要多写很多,个人认为不需要做这种意义不大的事。
所以,我们还是针对一些特定的场景才使用,比如某些任务需要很长的时间等等。
context.TODO()
那么,为什么gorm对数据库的操作又不需要这个参数呢?还没了解过,以后再说