对 setTimeout 的并发调用导致比预期更多的延迟
Posted
技术标签:
【中文标题】对 setTimeout 的并发调用导致比预期更多的延迟【英文标题】:Concurrent calls to setTimeout cause more delays than expected 【发布时间】:2014-09-03 01:45:25 【问题描述】:我正在做一个 Node 性能的小型演示,我很惊讶地看到以下示例对 setTimeout
进行 50 次并发调用需要超过 4 秒而不是 500 毫秒。
以下代码设置了一个非常简单的快速服务器,它监听所有请求,并在 500 毫秒后使用setTimeout
响应。然后它有一个客户端发出 50 个请求,传入一个数字来跟踪请求和相应的响应。
// SERVER
var express = require('express');
var app = express();
app.get('*', function (req, res)
setTimeout(function ()
return res.send(req.query.i);
, 500);
);
app.listen(8099);
// CLIENT
var http = require('http');
function start()
for (var i = 0; i < 50; i++)
console.log(new Date().getSeconds() + ':' + new Date().getMilliseconds() + ' - Sending ' + i);
http.get('http://localhost:8099?i=' + i, responseHandler);
function responseHandler(res)
res.on('data', function (body)
return console.log(new Date().getSeconds() + ':' + new Date().getMilliseconds() + ' - Received ' + body);
);
start();
我预计代码需要 30 到 50 毫秒才能完成所有 setTimeout
调用,然后在 500 毫秒后所有这些调用将在同一时间左右响应。相反,我看到他们以 5 人一组的方式响应,每组 5 人之间有 500 毫秒。
我尝试了一些更简单的替代方案,它们都可以解决问题。如果我取出setTimeout
并立即回复,所有 50 条回复都会在 100 毫秒内收到。如果我将 Web 服务器排除在外,只对 setTimeout
进行 50 次调用,那么所有 50 次调用都会立即排队,并且所有 50 次调用会在 500 毫秒后同时返回。
当 express 和 http 调用与 setTimeout
结合使用时,可能导致额外延迟的原因是什么?
【问题讨论】:
您遇到了这些延迟because of the event queue。 我可以确认这些发现。亚历克斯,这不是一个有用的评论:everything 异步与事件队列相关。说这是“因为事件队列”就像说“这是因为编程”。无意义的。我怀疑 Node 中的计时器函数有某种“元队列”。也就是说,在计时器队列中,Node 获取接下来的五个可用计时器并安排这些计时器进行处理。不过不确定……现在查看 Node 源代码。 我收回我说的话:我不认为这是setTimeout
的限制(因为,正如 Samuel 在他的帖子中所说,如果您在没有 HTTP 请求的情况下运行此测试,它会按预期工作)。所以这一定是 HTTP/Express 队列问题。
【参考方案1】:
这个问题与setTimeout
无关(当您在没有客户端/服务器设置的情况下运行测试时,您可能已经猜到了)。问题在于 Node http
服务器在任何给定时间将处理的打开 HTTP 套接字的数量。
控制它的设置是Agent.maxSockets
(有关详细信息,请参阅documentation)。
maxSockets
的默认值为 5。因此,如果您向您的应用发送 50 个请求,它会一次处理其中的 5 个直到它们完成,然后再继续处理新的请求。由于在 setTimeout
函数调用它们的回调之前它们无法完成,因此您的请求会以大约每 500 毫秒 5 次的突发响应得到响应。
如果你想证明这一点,你所要做的就是改变maxSockets
的值:
http.globalAgent.maxSockets = 10;
现在您将看到请求以 10 个突发的方式得到处理。如果您将其设置为 50,您应该会看到您正在寻找的行为。
请注意,它可能被设置为 5 有一个很好的理由,所以我不认为你的问题的“解决方案”只是将 maxSockets
设置为 Infinity
或类似的可怕的东西。
mscdex 在 cmets 中指出,您可以在 http.request
中指定一个 cusotm 代理,并且还会更改 Node v0.12 中的默认 maxSockets
。
【讨论】:
一些注意事项:应该更清楚地说明,您可以创建一个新的/自定义 Agent 实例,该实例具有自己的maxSockets
,您可以将其传递给 http.request()
而不是改变默认/全局代理的maxSockets
。此外,默认/全局代理 maxSockets
不会再被节点 v0.12 限制(到 5 个)。
感谢您的信息,mscdex。我会更新我的答案。
完美。那解决了它。对于我的测试,我将其设置为 50。我了解最大套接字的需求,但 5 非常小,特别是如果您有一个 I/O 密集型应用程序,其中大部分时间都在等待大量 I/O 或外部进程。
您也可以随时使用集群来启动多个实例。以上是关于对 setTimeout 的并发调用导致比预期更多的延迟的主要内容,如果未能解决你的问题,请参考以下文章