性能问题之前有多少并发setTimeout? [英] How many concurrent setTimeouts before performance issues?

查看:139
本文介绍了性能问题之前有多少并发setTimeout?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a node.js app with 10k-100k concurrent setTimeouts running at any given time. (They are all 5 minute duration.) The callback is pretty trivial, just an HDECRBY in redis. I have not hit any performance issues yet with this, even on a t2.micro instance.

我知道如果回调函数无法获取,我将遇到问题执行速度与我设置setTimeouts一样快(显然),但是有很多setTimeouts,本身有问题吗?例如,如果我将其扩展到例如100万并发,我是否会遇到RAM瓶颈? 1000万?

I know that I will run into issues if the callback functions can't get executed as fast as I'm setting the setTimeouts (obviously), but are there issues with having a high number of setTimeouts, per se? e.g., am I going to run in to a RAM bottleneck if I scale this up to, say, 1 million concurrent? 10 million?

推荐答案

对于这些类型的问题,看看node.js如何处理定时器通常很有用源代码

For these types of questions, it is often useful to just go look at how node.js handles timers in the source code.

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

What you will find is that node.js keeps one or more linked lists of its own internal timer objects and all timers set to occur at the same time share one libuv timer. This means that zillions of timers all set to occur in a fairly specific time window will inevitably share many firing times and thus will share timer lists and thus will share many system timer objects.

这使得拥有数以万计的计时器对象成为一个问题。现在,每个计时器对象仍然需要一些内存,并不是计时器实现中的每个操作都是恒定的时间,尽管你可以在下面的评论中看到,他们试图尽可能多地使它们成为允许大量计时器的恒定时间具有相当不错的性能。

This makes it less of an issue for having zillions of timer objects. Now, every timer object still requires some memory and not every operation in the timer implementation is of constant time though you can see in the comments below, they tried to make as many of them as possible to be constant time to allow large numbers of timers with still decent performance.

如果您在定时器触发时不需要绝对精度,则可能会导致定时器更加合并并更频繁地共享定时器对象仅为特定时间边界(例如偶数100ms)安排定时器。这将在相同的发射时间内安排更多的数万个计时器,并允许node.js将更多的计时器放入所有共享单个系统计时器的同一列表中。我不知道这对你的计时器是否可行,或者是否需要它,但在研究node.js如何工作时,它会提高效率。 node.js内部的计时器列表更少,libuv中的系统计时器更少。

If you don't need absolute precision in exactly when the timer fires, you can probably cause your timers to coalesce and share timer objects more often by scheduling your timers only for specific time boundaries such as an even number of 100ms. This would be scheduling more of your zillions of timers for the same firing time and would allow node.js to put more timers into the same list that all share a single system timer. I don't know whether this is feasible with your timers or if it is even needed, but in studying how node.js works, it would increase efficiency. There would be both fewer timer lists internally to node.js and fewer system timers in libuv.

以下是一些解释性注释来自定时器上的node.js代码,解释了设计的更多方面:

Here are some explanatory comments from the node.js code on timers that explains some more aspects of the design:

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

这篇关于性能问题之前有多少并发setTimeout?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆