我有一个简单的例子,我从node.js服务器请求不同的上游代理服务器。随着负载的增加,我看到请求需要花费大量的时间来执行(尽管我的上游代理服务器响应的时间在请求中是不变的)。为了证明这个问题,我已经编写了一个示例程序,如下所示。当我执行以下程序时,第一个请求执行 118ms ,最后一个请求 10970ms ,具体取决于您点击的网站(我已将网址更改为谷歌,用你最喜欢的网站试试吧)。如果您发现我使用异步来并行化我的请求。
问题是,node.js在并行运行时花了这么多时间来执行请求的原因是什么。为了给出更多关于infra设置的上下文(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
答案 0 :(得分: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);
}
);
}
此外,根据您使用的节点版本,http
模块可能会限制对特定主机名的并行请求数:
$ node -pe 'require("http").globalAgent.maxSockets'
在节点0.10上,默认值为5;在节点0.12上,默认值为Infinity
(&#34;无限制&#34;)。因此,如果您不在节点0.12上,则应在代码中增加该值:
var http = require('http');
http.globalAgent.maxSockets = Infinity;
...
答案 1 :(得分: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);
样本并没有真正优化,但仍然总共1000个请求与JXcore一起运行对我来说快一点(我能够在我的平台上测量高达20%的增益)。这可能因机器而异,因为多任务处理在一个进程中使用不同的线程/实例(不再需要集群)。我的机器只有4个线程,这就是我使用jxcore.tasks.setThreadCount(4);
的原因。您可以尝试使用32:)
处理每个请求的方式没有显着差异,所以我并不是说每个请求花费的时间更少,但是密钥可能隐藏在不同的排队机制中,与#34; async&#34相反;模块。当然,多亏了多任务处理。