性能问题之前有多less个并发setTimeout?

我有一个node.js应用程序,在任何给定的时间运行10k-100k并发setTimeouts。 (他们都是5分钟的时间。)callback是相当微不足道的,只是在redis中的HDECRBY。 我还没有遇到任何性能问题,即使在t2.micro的情况下。

我知道,如果callback函数无法像setTimeout(显然)那样快地执行,我会遇到问题,但是本身具有大量setTimeouts的问题呢? 比如,如果我把这个比例扩展到100万个并发数据库,​​我是否会遇到内存瓶颈? 千万?

对于这些types的问题,查看node.js如何处理源代码中的定时器通常是有用的。

你会发现node.js保留一个或多个自己的内部定时器对象的链表,并将所有定时器设置为同时发生一个libuv定时器。 这意味着所有设置为在相当特定的时间窗口中出现的计时器的数量将不可避免地共享许多发射时间,并且因此将共享计时器列表,并且因此将共享许多系统计时器对象。

这使得它不需要有大量的定时器对象。 现在,每个定时器对象仍然需要一些内存,并且定时器实现中的每个操作都不是固定时间的,尽pipe您可以在下面的注释中看到它们,但是他们试图尽可能多地使定时器保持恒定的时间以允许大量的定时器还有不俗的performance。

如果在计时器触发时不需要绝对的精确度,那么通过调度计时器只能在特定的时间范围内(例如100ms的偶数),可能会使计时器更经常地合并和共享计时器对象。 这将安排更多的定时器在同一个射击时间,并允许node.js把更多的定时器放在同一个列表中,所有的共享一个系统定时器。 我不知道你的定时器是否可行,或者甚至是需要的,但是在研究node.js如何工作时,会提高效率。 Node.js内部的定时器列表会更less,libuv中的系统定时器更less。


以下是定时器上的node.js代码的一些解释性注释,解释了devise的一些方面:

// 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.