Javascript 性能问题之前有多少并发设置超时?

Javascript 性能问题之前有多少并发设置超时?,javascript,node.js,performance,settimeout,Javascript,Node.js,Performance,Settimeout,我有一个node.js应用程序,在任何给定的时间运行10k-100k并发设置超时。(它们都有5分钟的持续时间。)回调非常简单,只是redis中的一个HDECRBY。我还没有遇到任何性能问题,即使是在t2.micro实例上 我知道,如果回调函数不能像设置设置超时那样快地执行,我会遇到问题(很明显),但是设置超时的数量太多本身会有问题吗?e、 例如,如果我将其扩展到100万并发,是否会遇到RAM瓶颈?1000万?对于这些类型的问题,只需看看node.js如何处理数据库中的计时器就可以了 您会发现no

我有一个node.js应用程序,在任何给定的时间运行10k-100k并发设置超时。(它们都有5分钟的持续时间。)回调非常简单,只是redis中的一个HDECRBY。我还没有遇到任何性能问题,即使是在t2.micro实例上


我知道,如果回调函数不能像设置设置超时那样快地执行,我会遇到问题(很明显),但是设置超时的数量太多本身会有问题吗?e、 例如,如果我将其扩展到100万并发,是否会遇到RAM瓶颈?1000万?

对于这些类型的问题,只需看看node.js如何处理数据库中的计时器就可以了

您会发现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.

nodejs中的计时器不是并发的。@zerkms从上下文中可以清楚地看出,它不是。不是消耗内存和CPU的计时器,而是您计划的工作。因此,您应该关注代码的工作量,而不是计时器。我提到我所有的计时器都是5分钟(30000ms),因此这意味着我所有的计时器都包含在一个链接列表中,并共享一个libuv计时器。很好。另一个我没有考虑的问题是存储所有回调函数。每个setTimeout都是在回调期间设置的,所以我必须使用闭包。。。这意味着内存中有数千个匿名函数,同一个函数的所有副本都有一个不同的变量。也许我不应该使用闭包(没有参数)“setTimeout(closure(x),30000)”,而是应该对每个setTimeout使用相同的函数,并使用闭包传递一个参数,如“setTimeout(func,30000,closure(x)),这样数千个匿名函数在内存中就更小了。@barron它们是按计时器类型组织的,不是按持续时间。@zerkms“对象映射被保留,其中包含按持续时间(毫秒)设置键的链表。“我肯定我在这里误解了什么。@barron-这取决于是否所有计时器都被设置为在同一时间设置为30000ms。”。计时器是按启动时间(计时器设置为启动的时钟时间)收集的,而不是按计时器设置后的持续时间收集的。因此,如果所有计时器都同时设置,那么它们将共享,但是如果它们在不同的时间设置,那么它们将在不同的时间触发,而不会被组织在一起。