HTTP客户端超时是一个时间限制,服务器必须在这个时间内处理请求并返回响应。如果超过了设定的超时时间,HTTP客户端应该取消请求并报告错误。这种机制对于在生产环境中运行的应用程序非常重要。它有助于防止应用程序在等待服务器响应时被卡住,而这可能需要很长的时间,或者根本就没有发生。
在Go中,标准的HTTP客户端默认不设置超时。这意味着一个应用程序可以永远等待服务器的响应。为了防止这种情况,你应该始终记得在你的HTTP客户端设置一个超时。而这正是我们在这里要做的 🙂。
在本文中,我们将介绍为HTTP请求设置超时的三种情况:
- 为所有的新请求设置相同的超时时间
http.Client. - 为每个新请求设置单独的时间限制。
- 修改许多外部API客户端包所使用的默认HTTP客户端的超时。
为每个HTTP客户端设置超时
如果你想为一个新的HTTP客户端的所有请求设置相同的超时时间,你可以使用。 http.Client的所有请求设置相同的超时,在初始化客户端时设置Timeout 字段,在这里你可以指定请求必须被处理的时间限制。
看一下这个例子:
| |
在第9-18 行,我们声明了一个新的简单服务器,其唯一的任务是通过调用 time.Sleep函数。在main() 函数的第一行,我们启动这个服务器。在剩下的几行中,我们创建了一个新的 http.ClientTimeout 字段设置为2秒,并向创建的服务器发送一个新的请求。
所以通过向一个暂停5秒的服务器发送一个超时2秒的请求,我们会得到一个超时错误。
输出:
2022/08/08 12:31:43 Get "http://localhost:8090/timeout": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
如果你在没有设置Timeout 的情况下运行应用程序,你将不会得到任何错误。
设置每个请求的超时
如果你想为一个单独的请求设置超时,请创建一个新的请求 Context使用 context.WithTimeout()函数创建一个新的请求。然后,用这个上下文作为参数创建一个新的请求,使用 http.NewRequestWithContext()构造函数创建一个新的请求:
package main
import (
"context"
"log"
"net/http"
"time"
)
// func server() {...} the same as in the first example...
func main() {
server()
httpClient := http.Client{}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://localhost:8090/timeout", nil)
if err != nil {
log.Fatal(err)
}
if _, err := httpClient.Do(req); err != nil {
log.Fatal(err)
}
}
该 context.Context是一个对象,它是HTTP客户端和服务器用来发送/接收请求范围的值、取消和截止日期信号给更深层的服务的通用Go概念。在我们的例子中,上下文是用来设置为一个给定的新请求获得响应的最后期限。
请注意,除了超时值之外,该 context.WithTimeout()函数还需要一个父级上下文作为参数,在我们的例子中,它是一个用 context.Background()函数创建的新的空上下文。因此,我们收到了上下文和取消函数来释放与之相关的资源,我们作为一个延迟函数调用cancel() 。
因为和前面的例子一样,我们向暂停5秒的服务器发送了一个有2秒超时的请求,我们会得到类似的错误信息。
输出:
2022/08/08 15:06:18 Get "http://localhost:8090/timeout": context deadline exceeded
exit status 1
为默认的HTTP客户端设置超时
有时你可以使用使用默认的HTTP客户端的代码。 http.DefaultClient.通常情况下,没有办法改变这段代码,使其使用自定义定义的HTTP客户端,例如,在外部包或必须保持向后兼容性的代码中。幸运的是,这并不意味着我们不能为这种客户端定义一个超时。下面的例子显示了如何做到这一点:
package main
import (
"log"
"net/http"
"time"
)
// func server() {...} the same as in the first example...
func main() {
server()
http.DefaultClient.Timeout = 2 * time.Second
if _, err := http.Get("http://localhost:8090/timeout"); err != nil {
log.Fatal(err)
}
}
你所要做的就是在默认的HTTP客户端中设置Timeout 字段,然后所有使用默认客户端的 http包中所有使用默认客户端的函数都会尊重这个超时。
在这个例子中,你得到的错误信息与你为每个HTTP客户端设置超时相同。
输出:
2022/08/09 09:48:58 Get "http://localhost:8090/timeout": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1