2014-07-12 33 views
2

我正在研究一個節點性能的小演示,並且很驚訝地看到下面的例子,併發調用setTimeout需要花費4秒而不是500ms。對setTimeout的併發調用導致比預期更多的延遲

以下代碼設置了一個非常簡單的express服務器,用於偵聽所有請求,並在稍後使用setTimeout響應500毫秒。然後它有一個客戶端,通過傳遞50個請求來跟蹤請求和相應的響應。

// SERVER 
var express = require('express'); 
var app = express(); 

app.get('*', function (req, res) { 
    setTimeout(function() { 
     return res.send(req.query.i); 
    }, 500); 
}); 

app.listen(8099); 

// CLIENT 
var http = require('http'); 

function start() { 
    for (var i = 0; i < 50; i++) { 
     console.log(new Date().getSeconds() + ':' + new Date().getMilliseconds() + ' - Sending ' + i); 
     http.get('http://localhost:8099?i=' + i, responseHandler); 
    } 

    function responseHandler(res) { 
     res.on('data', function (body) { 
      return console.log(new Date().getSeconds() + ':' + new Date().getMilliseconds() + ' - Received ' + body); 
     }); 
    } 
} 

start(); 

我預想的代碼,以30-50ms的讓所有的setTimeout電話,然後爲500ms以後他們都將在同一時間作出迴應。相反,我看到他們以5人組的形式響應,每組5人之間有500毫秒的響應。

我嘗試了許多更簡單的替代方法,它們都可以解決問題。如果我拿出setTimeout並立即回覆,所有50個回覆都會在100ms內進入。如果我將Web服務器排除在外,並且只有50個呼叫到setTimeout,那麼所有50個呼叫立即排隊,所有50個呼叫在500毫秒後同時返回。

當express和http調用與setTimeout結合使用時,可能會導致額外延遲的原因是什麼?

+0

您正在經歷那些延遲[因爲事件隊列](http://stackoverflow.com/questions/19822668/what-exactly-is-a-node-js-event-loop-tick)。 –

+0

我可以證實這些發現。亞歷克斯,這不是一個有用的評論:*所有*異步與事件隊列有關。說這是「因爲事件隊列」就好像在說「這是因爲編程」。無意義的。我懷疑Node中的定時器功能有某種「元隊列」。也就是說,在計時器隊列中,節點將採用接下來的五個可用計時器並計劃這些計時器進行處理。不知道,儘管...現在查看Node源代碼。 –

+1

我收回我說的話:我不認爲它是'setTimeout'的限制(因爲Samuel在他的文章中說過,如果你沒有HTTP請求運行這個測試,它可以按預期工作)。所以這必須是HTTP/Express隊列問題。 –

回答

1

該問題與setTimeout無關(因爲您在未經客戶端/服務器設置的情況下運行測試時可能已經猜到了)。問題是節點http服務器在任何給定時間將處理的開放HTTP套接字的數量。

控制此設置的設置是Agent.maxSockets(有關更多信息,請參閱documentation)。

maxSockets的默認值是五。因此,如果您嚮應用發送了50個請求,則它將在的時間處理其中的5個請求,直至完成,然後再轉到新的請求。由於它們在setTimeout函數調用其回調函數之前無法完成,因此您的請求會以大約每500毫秒五次的突發響應。

如果你想證明了這一點,所有你需要做的是改變maxSockets值:

http.globalAgent.maxSockets = 10; 

現在你會看到的請求得到10突發處理來代替。如果將其設置爲50,則應該看到您要查找的行爲。

請注意,由於相當好的原因,它可能被設置爲5,所以我不認爲您的問題的「解決方案」只是設置爲maxSocketsInfinity或者可怕的東西。

mscdex在註釋中指出您可以在http.request中指定一個cusotm代理,並且Node v0.12中的默認maxSockets也將發生更改。

+0

一些注意事項:應該更清楚地說明您可以創建一個新的/自定義代理實例,該實例擁有自己的'maxSockets',您可以將其傳遞給'http.request()',而不是改變默認/全局代理的'maxSockets '。此外,默認/全局代理'maxSockets'不會被節點v0.12限制(至5)。 – mscdex

+0

感謝您的信息,mscdex。我會更新我的答案。 –

+0

完美。這解決了它。對於我的測試,我將其設置爲50.我明白需要使用最大套接字,但5非常小,特別是如果您擁有I/O密集型應用程序,而大部分時間都在等待大量I/O或外部進程。 –