不要信任默认超时
原文地址:Don’t trust default timeouts
现代应用程序不会崩溃但是它们会挂起。其中一个主要原因是假设网络可靠但是其实并不是。
当您在没有设置超时的情况下进行网络调用时,您正在告诉您的代码,您百分之百确信该调用将成功。你真的愿意打赌吗?
如果您正处在一个永远不会返回的同步网络调用中,那么您的线程将永远占用。哎哟,不返回的异步网络调用也不是免费的哦。当然,您没有占用线程,但您正在泄漏套接字。任何值得使用的 HTTP 客户端库都使用套接字池来避免重新创建连接,然而这些池子的容量是有限的。与其他资源泄漏一样,没有套接字只是时间问题。当这种情况发生时,您的应用程序将在等待连接释放时卡住。
如果网络不可靠,为什么我们要继续创建以无限为默认超时的 api?有些 API 一开始甚至没有设置超时的方法!一个好的 API 应该容易用正确的方式使用,并且很难被错误的方式使用。当默认超时为无穷大时,客户机很容易自食其果。
如果你记得这篇文章中的一件事,那就这样做:永远不要使用 “无限” 作为默认超时。
让我们看一些具体的例子。
Javascript 的 XMLHttpRequest 是从服务器异步检索数据的 web API。它的默认超时为零,这意味着没有超时!
var xhr = new XMLHttpRequest();
xhr.open('GET', '/api', true);
// No timeout by default!
xhr.timeout = 10000;
xhr.onload = function () {
// Request finished
};
xhr.ontimeout = function (e) {
// Request timed out
};
xhr.send(null);
客户端超时与服务器端超时一样重要。浏览器存在为特定主机打开的最大套接字数。如果您发出永不返回的网络请求,则会耗尽套接字池。当池耗尽时,您将无法再连接到主机。
fetch Web API 是 XMLHttpRequest API 的现代替代品,它使用承诺。最初引入 API 时,根本没有办法设置超时!不过,浏览器最近增加了对 Abort API 的实验性支持,以支持超时。
const controller = new AbortController();
const signal = controller.signal;
const fetchPromise = fetch(url, {signal});
// No timeout by default!
setTimeout(() => controller.abort(), 10000);
fetchPromise.then(response => {
// Request finished
})
在 Python 领域,事情并没有那么乐观。请求库使用默认超时 infinity。
# No timeout by default!
response = requests.get('https://github.com/', timeout=10)
那 Go 呢?默认情况下,Go 的 HTTP 包也不使用超时。
var client = &http.Client{
// No timeout by default!
Timeout: time.Second * 10,
}
response, _ := client .Get(url)
适用于 Java 和.NET 的现代 HTTP 客户端做得更好,通常带有默认超时。例如,.Net Core 的 HttpClient 的默认超时为 100 秒。虽然时间比较宽松,但比根本没有超时要好得多。他们会有默认超时并不奇怪,因为这些语言用于构建需要对网络故障进行稳定处理的大规模分布式系统。没有超时的网络请求是分布式系统的最大沉默杀手。
记住这一点#
根据经验,网络调用时请务必设置超时时间。而且,如果您构建库,请始终设置合理的默认超时,并使它们可为您的客户端配置。
本作品采用《CC 协议》,转载必须注明作者和本文链接