Javascript 对setTimeout的并发调用会导致比预期更多的延迟

Javascript 对setTimeout的并发调用会导致比预期更多的延迟,javascript,node.js,performance,express,Javascript,Node.js,Performance,Express,我正在制作一个节点性能的小演示,并惊讶地看到下面的示例中有50个对setTimeout的并发调用需要4秒而不是500毫秒 下面的代码设置了一个非常简单的express服务器,用于侦听所有请求,并在500毫秒后使用setTimeout进行响应。然后,它有一个客户端,该客户端发出50个请求,并传入一个数字,以跟踪请求和相应的响应 // SERVER var express = require('express'); var app = express(); app.get('*', functio

我正在制作一个节点性能的小演示,并惊讶地看到下面的示例中有50个对
setTimeout
的并发调用需要4秒而不是500毫秒

下面的代码设置了一个非常简单的express服务器,用于侦听所有请求,并在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();
//服务器
var express=需要(“express”);
var-app=express();
app.get('*',函数(req,res){
setTimeout(函数(){
返回res.send(req.query.i);
}, 500);
});
app.listen(8099);
//客户
var http=require('http');
函数start(){
对于(变量i=0;i<50;i++){
log(new Date().getSeconds()+':'+new Date().getmillizes()+'-发送'+i');
http.get('http://localhost:8099?i=“+i,负责人);
}
功能响应管理器(res){
res.on(‘数据’、函数(正文){
返回console.log(new Date().getSeconds()+':'+new Date().getmillizes()+'-已收到'+正文);
});
}
}
start();
我预计代码执行所有
setTimeout
调用需要30-50毫秒,500毫秒后,所有调用都会在同一时间响应。相反,我看到他们5人一组做出反应,每组5人之间有500毫秒的间隔

我尝试了一些简单的替代方案,它们都能解决问题。如果我取出
setTimeout
并立即响应,所有50个响应都会在100ms内响应。如果我把web服务器从等式中去掉,只需要50次调用
setTimeout
,那么所有50次调用都会立即排队,而所有50次调用都会在500毫秒后同时返回


当express和http调用与
setTimeout
相结合时,是什么导致了额外的延迟?

该问题与
setTimeout
无关(在没有客户端/服务器设置的情况下运行测试时,您可能已经猜到了这一点)。问题是节点
HTTP
服务器在任何给定时间将处理的打开的HTTP套接字的数量

控制此操作的设置是Agent.maxSockets(有关详细信息,请参阅)

maxSockets
的默认值为5。因此,如果你向你的应用程序发送50个请求,它将一次处理其中5个,直到它们完成,然后再继续处理新的请求。由于在
setTimeout
函数调用回调之前,它们无法完成,因此您的请求大约每500毫秒会有五次响应

如果您想证明这一点,只需更改
maxSockets
的值:

http.globalAgent.maxSockets = 10;
现在,您将看到以10次为单位处理请求。如果将其设置为50,则应该可以看到所需的行为

请注意,设置为5可能是出于一个很好的原因,因此我不认为问题的“解决方案”只是将
maxSockets
设置为
Infinity
或类似的可怕设置


mscdex在评论中指出,您可以在
http.request
中指定cusotm代理,并且节点v0.12中的默认
maxSockets
也将发生更改。

您正在经历这些延迟。我可以确认这些发现。Alex,这不是一个有用的注释:所有异步操作都与事件队列相关。说这是“因为事件队列”就像说“因为编程”。无意义的我怀疑节点中的计时器函数有某种“元排队”。也就是说,在计时器队列中,节点获取接下来的五个可用计时器,并安排这些计时器进行处理。不确定,但是…现在看一下节点源代码。我收回我说过的话:我不认为这是
setTimeout
的限制(因为,正如Samuel在他的帖子中所说,如果您在没有HTTP请求的情况下运行此测试,它将按预期工作)。因此,这一定是HTTP/Express队列问题。需要注意的是:您可以创建一个新的/自定义代理实例,该实例具有自己的
maxSockets
,您可以将其传递到
HTTP.request()
,而不是修改默认/全局代理的
maxSockets
。另外,默认/全局代理
maxSockets
将不再受节点v0.12的限制(限制为5)。感谢您提供的信息,mscdex。我会更新我的答案。太好了。这就解决了问题。为了我的测试,我把它设为50。我理解对max sockets的需求,但5是非常小的,特别是如果你有一个I/O密集型应用程序,其中大部分时间都在等待大量I/O或外部进程。你也可以随时使用群集来启动多个实例。