4

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

问题是,node.js 在并行运行时需要这么多时间来执行请求的原因是什么。为了提供更多有关基础设施设置(centos 6.5)的上下文,我打开了从 1024 到 65535 的端口范围,将 fin_timeout 更改为 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');});  

下面给出更多的上下文是 ruby​​ 中的代码来做同样的事情。我知道我不能像苹果和苹果那样比较这两种语言。但这个想法是显示使用 ruby​​ 依次执行相同请求所需的时间。我没有看到按顺序发出的每个请求的响应时间有任何增加。所以,我怀疑使用节点的并行请求需要更多时间来响应请求(问题不是来自服务器响应,而是来自机器本身发送请求)

require 'rest_client'

 1000.times do |number|
   beginning = Time.now
   response = RestClient.get 'http://google.com'
   puts "Time elapsed #{Time.now - beginning} seconds"
 end
4

2 回答 2

3

一方面,您没有调用异步迭代器回调函数:

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

此外,根据您使用的 Node 版本,该http模块可能会限制对特定主机名发出的并行请求数:

$ node -pe 'require("http").globalAgent.maxSockets'

在节点 0.10 上,默认值为 5;在节点 0.12 上,默认值为Infinity("unlimited")。因此,如果您不在 Node 0.12 上,则应在代码中增加该值:

var http = require('http');
http.globalAgent.maxSockets = Infinity;
...
于 2015-04-07T10:55:06.033 回答
2

我尝试使用JXcore (Node.JS 的分支,以及现在在github上的一个开源项目)来运行您的场景,它提供多任务处理(以及许多其他新功能)。

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

该示例并没有真正优化,但对于我来说,使用 JXcore 运行的总共 1000 个请求仍然快一点(我能够在我的平台上测量到高达 20% 的增益)。这可能因机器而异,因为多任务处理在一个进程中使用不同的线程/实例(不再需要集群)。我的机器只有 4 个线程,这就是我使用jxcore.tasks.setThreadCount(4);. 你可以试试你的 32 :)

处理每个请求的方式没有显着不同,所以我并不是说每个请求花费的时间更少,但关键可能隐藏在与“异步”模块相反的不同排队机制中。当然还要感谢多任务处理。

于 2015-04-08T11:19:48.050 回答