为什么需要超时控制?
- 请求时间过长,用户侧可能已经离开本页面了,服务端还在消耗资源处理,得到的结果没有意义
- 过长时间的服务端处理会占用过多资源,导致并发能力下降,甚至出现不可用事故
Go 超时控制必要性
Go 正常都是用来写后端服务的,一般一个请求是由多个串行或并行的子任务来完成的,每个子任务可能是另外的内部请求,那么当这个请求超时的时候,我们就需要快速返回,释放占用的资源,比如goroutine,文件描述符等。
服务端常见的超时控制
- 进程内的逻辑处理
- 读写客户端请求,比如HTTP或者RPC请求
- 调用其它服务端请求,包括调用RPC或者访问DB等
没有超时控制会怎样?
为了简化本文,我们以一个请求函数 hardWork
为例,用来做啥的不重要,顾名思义,可能处理起来比较慢。
func hardWork(job interface{}) error {
time.Sleep(time.Minute)
return nil
}
func requestWork(ctx context.Context, job interface{}) error {
return hardWork(job)
}
这时客户端看到的就一直是大家熟悉的画面
绝大部分用户都不会看一分钟菊花,早早弃你而去,空留了整个调用链路上一堆资源的占用,本文不究其它细节,只聚焦超时实现。
下面我们看看该怎么来实现超时,其中会有哪些坑。
第一版实现
大家可以先不往下看,自己试着想想该怎么实现这个函数的超时,第一次尝试:
func requestWork(ctx context.Context, job interface{}) error {
ctx, cancel := context.WithTimeout(ctx, time.Second*2)
defer cancel()
done := make(chan error)
go func() {
do