Javascript 从node.js到外部系统的并行请求的增加需要更多的时间来响应

Javascript 从node.js到外部系统的并行请求的增加需要更多的时间来响应,javascript,node.js,node-async,Javascript,Node.js,Node Async,我有一个简单的例子,我从node.js服务器请求一个不同的上游代理服务器。随着负载的增加,我发现执行请求需要花费大量的时间(尽管从上游代理服务器响应请求所花费的时间在整个请求中是恒定的)。为了演示这个问题,我编写了一个示例程序,如下所示。当我执行以下程序时,第一个请求需要118ms执行,最后一个请求需要10970ms执行,具体取决于您访问的网站(我已将url更改为google,请使用您最喜欢的网站进行尝试)。如果您注意到我正在使用异步来并行化请求 问题是,node.js并行运行时执行请求花费这么

我有一个简单的例子,我从node.js服务器请求一个不同的上游代理服务器。随着负载的增加,我发现执行请求需要花费大量的时间(尽管从上游代理服务器响应请求所花费的时间在整个请求中是恒定的)。为了演示这个问题,我编写了一个示例程序,如下所示。当我执行以下程序时,第一个请求需要118ms执行,最后一个请求需要10970ms执行,具体取决于您访问的网站(我已将url更改为google,请使用您最喜欢的网站进行尝试)。如果您注意到我正在使用异步来并行化请求

问题是,node.js并行运行时执行请求花费这么多时间的原因是什么。为了提供有关infra设置(centos 6.5)的更多内容,我已将端口范围从1024扩展到65535,将fin_超时更改为15秒,并在sysctl.conf中为套接字启用tw_reuse=1

var http = require('http');
var uuid = require('node-uuid');
var async = require('async');

function callExternalUrl(){
    var uniqueId = uuid.v4();
    console.time(uniqueId);
    var options = {
        host: 'google.com',
        port: '80',
        path: '/',
        method: 'GET'
    };
    var req = http.request(options, function(res) {
        var msg = '';
        res.setEncoding('utf8');
        res.on('data', function(chunk) {
            msg += chunk;
            console.timeEnd(uniqueId);
        });
        res.on('end', function() {
        });
    });
    req.end();
}

function iterateAsync(callback){
    var iter = [];
    for(var i=0; i<1000; i++){
        iter[i] = i;
    }
    async.each(iter,
        function(item, callback) {
            callExternalUrl();
        },
        function(err) {
            callback(err);
        }
    );
}

iterateAsync(function(){console.log('done');});  

首先,您没有调用异步迭代器回调函数:

function callExternalUrl(asyncCallback) {
  ...
  res.on('end', function() {
    asyncCallback();
  });
  ...
}

function iterateAsync(callback) {
  var iter = [];
  for(var i=0; i<1000; i++){
      iter[i] = i;
  }
  async.each(iter,
      function(item, asyncCallback) { // <-- HERE
        callExternalUrl(asyncCallback);
      },
      function(err) {
        callback(err);
      }
  );
}
在节点0.10上,默认值为5;在节点0.12上,默认值为
无限
(“无限”)。因此,如果您不在节点0.12上,则应在代码中增加该值:

var http = require('http');
http.globalAgent.maxSockets = Infinity;
...

我尝试使用(fork of Node.JS,现在是一个开源项目)运行您的场景,它提供了多任务处理(以及许多其他新特性)

var任务=功能(项目){
var http=require('http');
var uuid=require('node-uuid');
var uniqueId=uuid.v4()+“-”+process.threadId;
控制台时间(唯一标识);
变量选项={
主持人:"google.com",,
端口:'80',
路径:“/”,
方法:“获取”
};
var req=http.request(选项、函数(res){
var msg='';
res.setEncoding('utf8');
res.on('data',函数(块){
msg+=chunk;
控制台。时间结束(唯一标识);
});
res.on('end',function(){
process.release();
});
});
请求结束();
process.keepAlive();
};
jxcore.tasks.setThreadCount(4);
控制台。时间(“总计”);
process.on('exit',function(){
控制台。时间结束(“总计”);
});
对于(变量i=0;i<1000;i++)
jxcore.tasks.addTask(task,i);
该示例并没有真正优化,但对我来说,使用JXcore运行的1000个请求总数要快一点(我能够在我的平台上测量到高达20%的增益)。这可能因机器而异,因为多任务处理在单个进程中使用不同的线程/实例(不再需要集群)。我的机器只有4个线程,这就是为什么我使用
jxcore.tasks.setThreadCount(4)。您可以尝试使用您的32:)


处理每个请求的方式并没有明显不同,所以我并不是说每个请求花费的时间更少,而是密钥可能隐藏在不同的队列机制中,与“异步”模块相反。当然,多亏了多任务。

Node.js在一个线程中运行,使用集群实现您想要的“并行化”。当然,我知道这一点。原始代码使用群集模块在32核上运行。但是我看不到模式中有什么主要的区别,第一个请求完成得非常快,而后面的请求需要花费大量的时间来执行。我怀疑这与node处理打开和关闭http连接的方式有关。可能与infra如何针对此类http请求进行微调也没有多大关系。@micnic Node可以很好地并行I/O,而不必求助于多个进程。robertklep:我在原始代码中执行回调。作为上面示例代码的一部分,我遗漏了它。正如您正确提到的,问题在于maxSockets。在发布节点0.10之前,我正在使用它,因此我将设置更改为9999999。有些人说这是怎么回事。当我把数字减到100时,它开始起作用了。我应该花一些时间仔细研究一下节点http代码,以了解为什么不遵守较高的数字(可能也是因为我的服务器h/w上的硬限制)。感谢您的帮助和即时回复。不要使用
Infinity
。大概200左右。不要遇到内核每个进程最大打开文件数或每个用户最大打开文件数限制。
$ node -pe 'require("http").globalAgent.maxSockets'
var http = require('http');
http.globalAgent.maxSockets = Infinity;
...
var task = function (item) {
  var http = require('http');
  var uuid = require('node-uuid');

  var uniqueId = uuid.v4() + "-" + process.threadId;
  console.time(uniqueId);
  var options = {
    host: 'google.com',
    port: '80',
    path: '/',
    method: 'GET'
  };
  var req = http.request(options, function (res) {
    var msg = '';
    res.setEncoding('utf8');
    res.on('data', function (chunk) {
      msg += chunk;
      console.timeEnd(uniqueId);
    });
    res.on('end', function () {
      process.release();
    });
  });
  req.end();
  process.keepAlive();
};

jxcore.tasks.setThreadCount(4);
console.time("total");
process.on('exit', function () {
  console.timeEnd("total");
});

for (var i = 0; i < 1000; i++)
  jxcore.tasks.addTask(task, i);