2014-11-23 58 views
1

當我運行下面的代碼9999999+時間,節點將返回:遞歸 - 節點的內存不足

FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory 
Aborted (core dumped) 

最新最好的解決方案來解決這個問題,不是增加最大ALLOC大小或任何其他命令線參數?

我想提高代碼質量而不是破解解決方案。

以下是應用程序內遞歸的主要批量。

該應用程序是一個負載測試工具。

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

你想用這段代碼實現什麼? – 2014-11-23 23:03:40

+0

這是一個負載測試應用程序。 – 2014-11-23 23:07:33

+0

使用多機械化代替http://testutils.org/multimechanize/ – peter 2014-11-23 23:09:26

回答

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變量。

+0

很好的答案,謝謝! – 2014-11-23 23:15:20

+1

我認爲'for'循環仍然會導致內存不足錯誤,因爲執行的迭代次數... – 2014-11-23 23:18:34

+0

'async.queue'不是遞歸的。內存在請求之間釋放。 – Daniel 2014-11-23 23:24:36

1

您將並行啓動10億個「客戶端」,並讓它們中的每一個以遞歸方式遞歸地執行http獲取請求。

幾句話:

  • ,而你的問題中提到千萬的客戶,您的代碼創建1個十億客戶。
  • 您應該用遞歸函數替換for循環,以消除內存不足錯誤。

東西在這幾行:

a.prototype.createClients = function(i){ 
    if (i < 999999999) { 
     this.recursiveRequest(); 
     this.createClients(i+1); 
    } 
} 
  • 然後,你可能要包括一些延遲客戶創造之間,或調用recursiveRequest之間。使用setTimeout
  • 你應該有一個辦法讓遞歸停止(onSuccessrecursiveRequest保持通話對方)
  • 流量控制庫一樣async Node.js的模塊可幫助。
+0

我同意,我覺得這個和上面的帖子將會是我的問題的解決方案,我會在完成後跟上答案。 – 2014-11-23 23:22:15

0

千萬是非常大的......假設堆棧支持的任何號碼的呼叫,它應該的工作,但你可能會問,JavaScript解釋器來加載千萬X相當多的記憶.. 。結果是內存不足。

另外我個人不明白爲什麼你想同時有這麼多的請求(測試服務器上的重負載?)一種優化的方法是不創建你正在做的「浮動函數」很多。 「浮動函數」在每個實例化中使用自己的一組內存。

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函數中聲明的所有函數。特別是如果他們非常大。

如果你想使用this指針(請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(); } 
0

據丹尼爾評論,你的問題是,你濫用一個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萬次,您需要檢查已發送的請求數。

我的「修復」假設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(); 
} 

注意,你將力推毫秒的時間緩衝1000萬次:您還可以更改代碼,以利用暫停標誌爲英寸那是一張大桌子!你可能想要一個總計,並在最後計算平均值:

this.time += ms; 

    // at the end: 
    this.average_time = this.time/this.successful;