现代应用程序不会崩溃;他们挂了。其主要原因之一是假设网络是可靠的。它不是。
当你在没有设置超时的情况下进行网络调用时,你是在告诉你的代码你 100% 确信调用会成功。你真的会接受这个赌注吗?
如果您是一个永远不会返回的同步网络调用,那么至少您的线程会永远占用。哎呀。不返回的异步网络调用也不是免费的。当然,您不是在占用线程,而是在泄漏套接字。任何值得一提的 HTTP 客户端库都使用套接字池来避免重新创建连接。这些池的容量有限。像任何其他资源泄漏一样,没有套接字只是时间问题。发生这种情况时,您的应用程序将卡住等待连接释放。
如果网络不可靠,为什么我们要不断创建默认超时为无穷大的 API?有些 API 一开始甚至没有办法设置超时!一个好的 API 应该易于以正确的方式使用,并且难以以错误的方式使用。当默认超时为无穷大时,客户很容易自责。
如果你还记得这篇文章中的一件事,那就这样吧:永远不要使用“infinity”作为默认超时。
让我们看一些具体的例子。
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 是使用 Promises 的 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 领域的情况并没有那么乐观。requests 库使用infinity的默认超时。
# No timeout by default!
response = requests.get('https://github.com/', timeout=10)
围棋呢?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 秒。这很松散,但总比没有超时要好得多。这并不奇怪,因为这些语言用于构建需要对网络故障具有鲁棒性的大规模分布式系统。没有超时的网络请求是分布式系统的头号杀手。
记住这一点
根据经验,在进行网络调用时始终设置超时。如果您构建库,请始终设置合理的默认超时并为您的客户配置它们。