2016-07-08 42 views
1

我有一個node.js應用程序,在任何給定的時間運行10k-100k併發setTimeouts。 (他們都是5分鐘的持續時間。)回調是非常微不足道的,只是redis中的HDECRBY。我還沒有遇到任何性能問題,即使是在t2.micro的情況下。性能問題之前有多少個併發setTimeouts?

我知道我會遇到問題,如果回調函數不能得到儘可能快,因爲我設置一個定時器,(顯然)執行,但是否有問題,具有高數下一個定時器的,本身 ?例如,如果我將這個比例擴展到100萬併發,我是否會遇到RAM瓶頸?千萬?

+0

在計時器的NodeJS不是*併發*。 – zerkms

+0

@zerkms上下文中的含義很明確。 – barron

+0

不是。不是使用內存和CPU的計時器,而是您已安排的工作。所以你應該關心你的代碼做了多少工作,而不是定時器。 – zerkms

回答

4

對於這些類型的問題,查看node.js如何處理source code中的定時器通常很有用。

你會發現,node.js保留一個或多個鏈接列表,它自己的內部定時器對象和所有定時器設置爲同時發生共享一個libuv定時器。這意味着所有設置爲在相當特定的時間窗口中出現的定時器的數量將不可避免地共享許多定時器列表,並且因此將共享定時器列表,並且因此將共享許多系統定時器對象。

這使得它不需要有大量的計時器對象。現在,每個計時器對象仍然需要一些內存,並且儘管您可以在下面的註釋中看到,但並不是每個計時器實現中的操作都是恆定時間,但他們儘可能多地使它們保持恆定的時間以允許大量的計時器還有不俗的表現。

如果您在計時器觸發時不需要絕對精度,那麼您可能會通過僅爲特定時間範圍(例如偶數個100ms)安排計時器,使得計時器更經常地合併和共享計時器對象。這將在同一個觸發時間內調度更多的定時器,並允許node.js將更多定時器放入同一個列表中,以便共享一個系統定時器。我不知道這對您的定時器是否可行,或者甚至是需要的,但是在研究node.js如何工作時,它會提高效率。 Node.js內部的定時器列表會更少,libuv中的系統定時器更少。


以下來自定時器Node.js的代碼,以解釋設計的一些方面的一些解釋性意見:

// HOW and WHY the timers implementation works the way it does. 
// 
// Timers are crucial to Node.js. Internally, any TCP I/O connection creates a 
// timer so that we can time out of connections. Additionally, many user 
// user libraries and applications also use timers. As such there may be a 
// significantly large amount of timeouts scheduled at any given time. 
// Therefore, it is very important that the timers implementation is performant 
// and efficient. 
// 
// Note: It is suggested you first read though the lib/internal/linkedlist.js 
// linked list implementation, since timers depend on it extensively. It can be 
// somewhat counter-intuitive at first, as it is not actually a class. Instead, 
// it is a set of helpers that operate on an existing object. 
// 
// In order to be as performant as possible, the architecture and data 
// structures are designed so that they are optimized to handle the following 
// use cases as efficiently as possible: 

// - Adding a new timer. (insert) 
// - Removing an existing timer. (remove) 
// - Handling a timer timing out. (timeout) 
// 
// Whenever possible, the implementation tries to make the complexity of these 
// operations as close to constant-time as possible. 
// (So that performance is not impacted by the number of scheduled timers.) 
// 
// Object maps are kept which contain linked lists keyed by their duration in 
// milliseconds. 
// The linked lists within also have some meta-properties, one of which is a 
// TimerWrap C++ handle, which makes the call after the duration to process the 
// list it is attached to. 
// 
// 
// ╔════ > Object Map 
// ║ 
// ╠══ 
// ║ refedLists: { '40': { }, '320': { etc } } (keys of millisecond duration) 
// ╚══   ┌─────────┘ 
//    │ 
// ╔══   │ 
// ║ TimersList { _idleNext: { }, _idlePrev: (self), _timer: (TimerWrap) } 
// ║   ┌────────────────┘ 
// ║ ╔══ │       ^
// ║ ║ { _idleNext: { }, _idlePrev: { }, _onTimeout: (callback) } 
// ║ ║  ┌───────────┘ 
// ║ ║  │        ^
// ║ ║  { _idleNext: { etc }, _idlePrev: { }, _onTimeout: (callback) } 
// ╠══ ╠══ 
// ║ ║ 
// ║ ╚════ > Actual JavaScript timeouts 
// ║ 
// ╚════ > Linked List 
// 
// 
// With this, virtually constant-time insertion (append), removal, and timeout 
// is possible in the JavaScript layer. Any one list of timers is able to be 
// sorted by just appending to it because all timers within share the same 
// duration. Therefore, any timer added later will always have been scheduled to 
// timeout later, thus only needing to be appended. 
// Removal from an object-property linked list is also virtually constant-time 
// as can be seen in the lib/internal/linkedlist.js implementation. 
// Timeouts only need to process any timers due to currently timeout, which will 
// always be at the beginning of the list for reasons stated above. Any timers 
// after the first one encountered that does not yet need to timeout will also 
// always be due to timeout at a later time. 
// 
// Less-than constant time operations are thus contained in two places: 
// TimerWrap's backing libuv timers implementation (a performant heap-based 
// queue), and the object map lookup of a specific list by the duration of 
// timers within (or creation of a new list). 
// However, these operations combined have shown to be trivial in comparison to 
// other alternative timers architectures. 


// Object maps containing linked lists of timers, keyed and sorted by their 
// duration in milliseconds. 
// 
// The difference between these two objects is that the former contains timers 
// that will keep the process open if they are the only thing left, while the 
// latter will not. 
+0

我提到我所有的定時器都是5分鐘持續時間(30000ms),所以這應該意味着我的所有定時器都包含在單個鏈表中並共享一個libuv定時器。大。 – barron

+0

我沒有想過的另一個問題是存儲所有的回調函數。每個setTimeout都是在回調期間設置的,所以我不得不使用閉包...所以這意味着有成千上萬的匿名函數在內存中浮動,相同函數的所有副本只是使用不同的變量。也許而不是使用閉包(沒有參數)「setTimeout(closure(x),30000)」,我應該對每個setTimeout使用相同的函數,並使用閉包傳遞參數,如「setTimeout(func,30000,closure (x))「,以便成千上萬的匿名函數在內存中更小。 – barron

+0

@barron它們按計時器類型組織,而不是持續時間。 – zerkms