你意想不到的默认超时

有时我们会发现很多应用它不会崩溃,但是很多时候它会hang住。而hang住的一个很重要的原因就是假设网络是可靠的。而事实上却不是。

所以,当你做一个网络调用而没有设置超时的,就意味着你100%确认这个调用会成功,真的可以这么自信吗?哈哈。

假如是一个同步的调用,如果这个调用永远不返回,则显然会永远卡住。不过实际上我们都是使用的异步调用,是的,一个异步调用的不返回是不会卡住主进程的。但是有一个新的问题,就是它会消耗socket。因为很多HTTP客户端的库都使用socket池,这样就不需要每次都创建连接(毕竟创建连接是需要时间的)。一般来说,这种socket池也是有大小限制的,这也就意味着每一次不返回的调用都会消耗socket池中的一个已有的连接。所以,但这种情况发生了比较多了之后,你的应用就会卡在等待连接的释放。

假如你的网络不可靠,我们为什么总是要创建一个不超时的API呢?很多API调用甚至每有一个很方便的设置tiemout的地方,尤其是初始化的时候,你要设置timeout就需要额外进行调用。尤其是这种API很多时候默认的timeout还是无限的。当然,一个好的API都有很方便的设置timeout的方法。

所以,第一个建议就是永远不用把默认的timeout设为无限。

我们下面来看一个例子:

我们经常会使用Javascript中的XMLHttpRequest这一个web API。它的默认timeout是0,也就是没有timeout:

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);

现代web应用中可以使用fetch来取代XMLHttpRequest API,它使用的是promise。当它刚实现的时候,根本就没有方法来设置timeout,不过最近浏览器加入了一个Abort的API来设置timeout。

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方面也是类似的,request库也默认使用无限作为timeout的。

# No timeout by default!
response = requests.get('https://github.com/', timeout=10)

那Go怎么样呢?其实也是一样的,默认没有timeout

var client = &http.Client{
  // No timeout by default!
  Timeout: time.Second * 10, 
}
response, _ := client .Get(url)

当然在Java和.NET的世界则会更加好一点,它们都有默认的timeout。比如.Net Core的HttpClient有100s的默认时间,虽然很难说这个值好不好,但总归比没有好。

总结

所以总的来说,不管默认的timeout是多少,我们最好都设置自己想要的timeout。

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *