recursion – 节点内存不足
当我运行以下代码9999999+次时,Node返回:
FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory Aborted (core dumped)
除了增加最大分配大小或任何命令行参数之外,解决此问题的最佳解决scheme是什么?
我想提高代码质量,而不是破解一个解决scheme。
以下是应用程序内recursion的主要部分。
该应用程序是一个负载testing工具。
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(); }
看起来你真的应该使用一个队列,而不是recursion调用。 async.queue
为处理asynchronous队列提供了一个绝妙的机制。 您还应该考虑使用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}); }
您可以根据您想要与testing服务器连接的同时请求的数量来设置parallel_requests
variables。
您将并行启动10亿个“客户端”,并且以无限recursion的方式recursion地执行http获取请求。
几句话:
- 而你的问题提到1000万个客户,你的代码创造了10亿个客户。
- 您应该用recursion函数replace
for
循环,以消除内存不足的错误。
这些行中的东西:
a.prototype.createClients = function(i){ if (i < 999999999) { this.recursiveRequest(); this.createClients(i+1); } }
- 然后,你可能想在客户端创build之间或在
recursiveRequest
请求的调用之间包含一些延迟。 使用setTimeout
。 - 你应该有一个方法来获取recursion停止(
onSuccess
和recursiveRequest
请求不断调用对方) - 像asynchronous node.js模块的stream量控制库可能会有所帮助。
1000万是非常大的…假设堆栈支持任意数量的调用,它应该可以工作,但是您可能会要求JavaScript解释器加载1000万x相当多的内存……结果是内存不足。
另外我个人也不明白为什么你想要同时有这么多的请求(testing服务器上的负载很重)?一种优化的方法是不创build你正在做的很多“浮动函数”。 “浮动函数”在每个实例上使用自己的一组内存。
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: ar = function(resp) {...}; this.http.get(this.options, ar ...);
至less你会保存所有的function内存。 当然,这适用于在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: ar = function(resp) { a.instance.<func>(); }
使用实例可以从静态r
函数访问对象的函数。 这可能是可以充分利用this
参考的实际实施:
ar = function(resp) { a.instance.r_impl(); }
根据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){ ...
您的recursion性足以运行testing任何次数。
不过,你所做的永远不会放弃。 你有一个halt
variables,但它看起来不像你曾经设置为true。 但是,要testing一千万次,您需要检查已发送的请求数量。
我的“修复”假设onError()失败(不recursion)。 您还可以更改代码以使用暂停标志,如下所示:
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;