当我运行以下代码9999999次时,Node返回:
FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory
Aborted (core dumped)
除了增加max alloc size或任何命令行参数之外,什么是解决此问题的最佳解决方案?
我希望提高代码质量,而不是破解解决方案。
以下是应用程序中主要的递归。
该应用程序是一种负载测试工具。
a.prototype.createClients = function(){
for(var i = 0; i < 999999999; i++){
this.recursiveRequest();
}
}
a.prototype.recursiveRequest = function(){
var self = this;
self.hrtime = process.hrtime();
if(!this.halt){
self.reqMade++;
this.http.get(this.options, function(resp){
resp.on('data', function(){})
.on("connection", function(){
})
.on("end", function(){
self.onSuccess();
});
})
.on("error", function(e){
self.onError();
});
}
}
a.prototype.onSuccess = function(){
var elapsed = process.hrtime(this.hrtime),
ms = elapsed[0] * 1000000 + elapsed[1] / 1000
this.times.push(ms);
this.successful++;
this.recursiveRequest();
}
答案 0 :(得分:1)
看起来你应该使用队列而不是递归调用。 async.queue
为处理异步队列提供了一种很棒的机制。您还应该考虑使用request
模块使您的http客户端连接更简单。
var async = require('async');
var request = require('request');
var load_test_url = 'http://www.testdomain.com/';
var parallel_requests = 1000;
function requestOne(task, callback) {
request.get(task.url, function(err, connection, body) {
if(err) return callback(err);
q.push({url:load_test_url});
callback();
});
}
var q = async.queue(requestOne, parallel_requests);
for(var i = 0; i < parallel_requests; i++){
q.push({url:load_test_url});
}
您可以根据要与测试服务器匹配的同时请求数来设置parallel_requests
变量。
答案 1 :(得分:1)
您正在推出10亿&#34;客户&#34;并行地,让它们中的每一个在无限递归中递归地执行http get请求。
几点评论:
for
循环,以消除内存不足错误。这些内容:
a.prototype.createClients = function(i){
if (i < 999999999) {
this.recursiveRequest();
this.createClients(i+1);
}
}
recursiveRequest
的调用之间包含一些延迟。使用setTimeout
。onSuccess
和recursiveRequest
继续相互呼叫)答案 2 :(得分:0)
1000万非常大...假设堆栈支持任意数量的调用,它应工作,但你可能要求JavaScript解释器加载1000万x相当多的内存......结果是内存不足。
另外,我个人不明白为什么你想要同时拥有这么多请求(测试服务器上的重负载?)一种优化方法是不创建&#34;浮动函数&# 34;你做了很多。 &#34;浮动功能&#34;在每个实例化中使用自己的内存集。
this.http.get(this.options, function(resp){ ... });
^^^^
++++--- allocates memory x 10 million
这里function(resp)...
声明在每次调用时分配更多内存。你想要做的是:
# either global scope:
function r(resp) {...}
this.http.get(this.options, r ...);
# or as a static member:
a.r = function(resp) {...};
this.http.get(this.options, a.r ...);
至少你会节省所有功能记忆。当然,这适用于您在 r 函数中声明的所有函数。特别是如果它们非常大。
如果你想使用这个指针(make r
一个原型函数),你可以这样做:
a.prototype.r = function(resp) {...};
// note that we have to have a small function to use 'that'... probably not a good idea
var that = this;
this.http.get(this.options, function(){that.r();});
要避免that
引用,您可以使用保存在全局中的实例。尽管如此,这仍然无法使用对象:
a.instance = new a;
// r() is static, but can access the object as follow:
a.r = function(resp) { a.instance.<func>(); }
使用该实例,您可以从静态r
函数访问对象的函数。这可能是可以充分利用this
引用的实际实现:
a.r = function(resp) { a.instance.r_impl(); }
答案 3 :(得分:0)
根据Daniel的评论,您的问题是您滥用for()
来计算您要发送的请求总数。这意味着您可以对代码应用非常简单的修复,如下所示:
a.prototype.createClients = function(){
this.recursiveRequest();
};
a.prototype.recursiveRequest = function(){
var self = this;
self.hrtime = process.hrtime();
if(!this.halt && this.successful < 10000000){
...
您的递归足以进行任何次数的测试。
但你做的事情永远不会放弃。你有一个halt
变量,但看起来你没有将它设置为true。但是,要测试1000万次,您需要检查已发送的请求数。
我的&#34;修复&#34;假设onError()失败(没有递归)。您还可以更改代码以使用暂停标志,如下所示:
a.prototype.onSuccess = function(){
var elapsed = process.hrtime(this.hrtime),
ms = elapsed[0] * 1000000 + elapsed[1] / 1000
this.times.push(ms);
this.successful++;
if(this.successful >= 10000000)
{
this.halt = true;
}
this.recursiveRequest();
}
请注意,您将在时间缓冲区推送ms一千万次。这是一张大桌子!您可能希望获得总数并在最后计算平均值:
this.time += ms;
// at the end:
this.average_time = this.time / this.successful;