2011-06-01 563 views
8

這裏是我的示例代碼:Node.js Http.request在負載測試下變慢。難道我做錯了什麼?

var http = require('http'); 

var options1 = { 
      host: 'www.google.com', 
      port: 80, 
      path: '/', 
      method: 'GET' 
     }; 

http.createServer(function (req, res) { 

     var start = new Date(); 
     var myCounter = req.query['myCounter'] || 0; 

     var isSent = false; 
     http.request(options1, function(response) { 
      response.setEncoding('utf8'); 
      response.on('data', function (chunk) { 
       var end = new Date(); 
       console.log(myCounter + ' BODY: ' + chunk + " time: " + (end-start) + " Request start time: " + start.getTime()); 

       if (! isSent) { 
        isSent = true; 
        res.writeHead(200, {'Content-Type': 'application/xml'}); 
        res.end(chunk); 
       } 
      }); 
     }).end(); 


}).listen(3013); 

console.log('Server running at port 3013'); 

我發現了什麼是,如果我連接到其他服務器,(谷歌或其他),響應會越來越慢到幾秒鐘。如果我連接到同一網絡中的另一個node.js服務器,則不會發生這種情況。

我使用JMeter進行測試。 1000個循環每秒50個併發。

我不知道是什麼問題...

=========================

進一步調查:

我在Rackspace和EC2上運行相同的腳本進行測試。腳本將使用http.request連接到:Google,Facebook,以及我的另一個腳本,它可以簡單地輸出由另一個EC2實例託管的數據(如hello world)。

測試工具我只是我桌面上的jMeter。

Pre-node.js測試: jMeter - > Google結果:快速且一致。 jMeter - > Facebook的結果:快速和一致。 jMeter - >我的簡單輸出腳本結果:快速和一致。

然後,我製作了50個並行線程/秒,帶有100個循環,測試了我的Rackspace nodejs,然後EC2 node.js,它具有相同的性能問題 jMeter - > node.js - > Google結果:在200個需求中從50毫秒變爲2000毫秒。
jMeter - > node.js - > Facebook結果:從200 ms到200 ms後需要3000 ms。
jMeter - > node.js - >我的簡單輸出腳本結果:在200個請求之後,從100 ms到1000 ms。
前10-20個請求很快,然後開始放慢速度。

然後,當我更改爲10個併發線程時,事情開始發生變化。響應非常一致,沒有減慢。

與Node.js(http.request)可以處理的併發線程數量有關。

------------ --------------

今天我做了更多的測試,在這裏,它是: 我用http.Agent並增加最大套接字。然而,有趣的是,在一臺測試服務器(EC2)上,它提高了很多,並且沒有更慢的速度。 HOwever,其他服務器(機架空間)只有一點改進。它仍然顯示出放緩。我甚至在請求標題中設置了「Connection:close」,它只提高了100ms。

如果http.request使用連接池,如何增加它?

在這兩個服務器,如果我做 「的ulimit -a」,打開文件的#1024

------------- ** 更多更多 * * -------------------

看來,即使我把maxSockets設置爲更高的數字,它只能在一定的限制。似乎有一個內部或OS依賴的套接字限制。但是要碰撞它?

------------- **經過廣泛的測試** ---------------

閱讀大量帖子後,我發現:



轉引自:https://github.com/joyent/node/issues/877


1)如果我設置的標頭與連接= '保活',表現還是不錯的,可以上去到maxSocket = 1024(這是我的Linux設置)。

var options1 = { 
        host: 'www.google.com', 
        port: 80, 
        path: '/', 
        method: 'GET', 
    **headers: { 
      'Connection':'keep-alive' 
    }** 
       }; 

如果我將它設置爲「連接」:「關閉」,響應時間會慢100倍。

有趣的事情發生在這裏:

在EC2 1),當我第一次測試與連接:保持活躍,這將需要大約20-30毫秒。然後,如果我更改爲連接:關閉或設置代理:false,響應時間將減慢到300毫秒。 WIHTOUT重新啓動服務器,如果我更改爲Connection:再次保持活動狀態,則響應時間將進一步減慢到4000毫秒。要麼我必須重新啓動服務器或等待一段時間才能恢復20-30ms的照明速度響應。 2)如果我使用agent運行它:false,首先,響應時間將減慢到300ms。但是,它會再次變得更快,並回到「正常」。

我的猜測是連接池仍然有效,即使你設置了agent:false。但是,如果您保持連接:保持連接,那麼它肯定會很快。只是不要切換它。




7月25日更新,2011

我試圖與http.js & https.js最新的node.js V0.4.9從https://github.com/mikeal/node/tree/http2

的修復性能更好更穩定。

+0

而不是做'isSent'魔法,你應該監聽response.on('end')'事件。 – 2011-06-02 00:30:44

+0

感謝您的建議。 :) – murvinlai 2011-06-04 01:13:09

+0

我遇到同樣的問題,並看到保持活着的變化相當大的性能增益,但是在負載測試後的一段時間我遇到了錯誤。 下面是錯誤: {[錯誤:連接EADDRNOTAVAIL] 代碼: 'EADDRNOTAVAIL', 錯誤號: 'EADDRNOTAVAIL', 系統調用: '連接'} – firemonkey 2013-04-30 15:16:03

回答

1

這不會neccesarily解決您的問題,但它清理你的代碼了一下,利用各種事件中,你應該的樣子:

var http = require('http'); 

var options1 = { 
     host: 'www.google.com', 
     port: 80, 
     path: '/', 
     method: 'GET' 
}; 

http.createServer(function (req, res) { 
    var start = new Date(); 
    var myCounter = req.query['myCounter'] || 0; 

    http.request(options1, function(response) { 
     res.on('drain', function() { // when output stream's buffer drained 
      response.resume(); // continue to receive from input stream 
     }); 
     response.setEncoding('utf8'); 
     res.writeHead(response.statusCode, {'Content-Type': 'application/xml'}); 
     response.on('data', function (chunk) { 
      if (!res.write(chunk)) { // if write failed, the stream is choking 
       response.pause(); // tell the incoming stream to wait until output stream drained 
      } 
     }).on('end', function() { 
      var end = new Date(); 
      console.log(myCounter + ' time: ' + (end-start) + " Request start time: " + start.getTime()); 
      res.end(); 
     }); 
    }).end(); 
}).listen(3013); 

console.log('Server running at port 3013'); 

我體內取出的輸出。由於我們是從一個套接字流到另一個套接字,所以我們無法確保在任何時候都看不到整個身體,而無需緩衝它。

編輯:我相信節點使用http.request連接池。如果您有50個併發連接(因此有50個併發http.request嘗試),您可能會遇到連接池限制。我目前沒有時間爲你查找,但你應該看看有關http的節點文檔,特別是http代理。

編輯2:關於node.js郵件列表中的一個非常類似的問題,有a thread。你應該看看它,特別是Mikael的帖子應該是有趣的。他建議通過將選項agent: false傳遞給http.request調用來完全關閉連接池。我沒有任何進一步的線索,所以如果這沒有幫助,也許你應該嘗試在node.js郵件列表上獲得幫助。

+0

仍然是同樣的事情。我在Rackspace服務器上運行,現在,我將嘗試在EC2服務器上查看它是否有任何區別。該死的...... :( – murvinlai 2011-06-02 20:42:30

+0

請注意編輯我的答案 – 2011-06-03 06:25:56

+0

嗨,我今天做了一個測試,請在我上面的原始文章中看到我的更新:) – murvinlai 2011-06-03 08:11:36

1

Github的問題877可能涉及:

https://github.com/joyent/node/issues/877

雖然這不是我清楚,如果這是你打的是什麼。當我點擊「代理:虛假」解決方法時,爲我工作,就像在請求中設置「連接:保持活動」標題一樣。

+0

我發現的是,如果set agent:false with connection:keep-alive,速度是一致的,但有點慢。如果代理設置爲連接:保持活動狀態,有時候,最初的幾個請求會比較慢,但它會很快。 (比代理快:false)。 如果連接沒有設置,或連接:關閉,那麼這將是一場災難。 – murvinlai 2011-06-07 17:07:11

8

我解決了這個問題

require('http').globalAgent.maxSockets = 100000 

agent = new http.Agent() 
agent.maxSockets = 1000000 # 1 million 
http.request({agent:agent}) 
1

我用同樣的問題所困擾。我發現我的瓶頸是DNS的東西,雖然我不清楚在哪裏/爲什麼。如果我的要求是http://myserver.com/asd,我幾乎無法運行50-100 rq/s,如果我超過100 rq/s並且有更多的事情變成災難,響應時間變得很大,並且一些請求永遠不會完成並且無限期地等待。需要殺死-9我的服務器。如果我向服務器的IP地址發送請求,所有內容都穩定在500 rq/s,儘管不完全平滑,並且圖形(我有實時圖形)陡峭。並且要注意,Linux中打開文件的數量仍然有限,並且我設法擊中了一次。另一個觀察結果是單節點過程不能平穩地達到500 rq/s。但是我可以啓動4個節點進程,每個節點進程達到200 rq/s,並且獲得非常平滑的圖形和一致的CPU /淨負載以及非常短的響應時間。這是節點0.10.22。

+1

原來這是DNS的nodejs錯誤。我不能肯定地說,但從我的代碼檢查來看,似乎有一種情況,在事件發生後可以分配「dns resolved」回調,但在發現在不穩定版本中這個方向發生了變化後,我停止深入挖掘節點的問題並沒有顯示在那裏。似乎固定在節點0.11.9,我的無限請求問題沒有在那裏顯示。 – bobef 2013-11-29 17:48:31