在设置大量计时器或使用计划任务队列之间寻找解决方案

Looking for a solution between setting lots of timers or using a scheduled task queue

我正在构建一项服务(游戏),其中一组用户将在一个房间中,每个用户将在一定时间内(例如 30 秒)做出选择。如果用户在这段时间内没有做出选择,该服务将自动为该用户生成一个选择。如果在持续时间结束之前做出选择,则下一个用户已启动并且计时器应重置。

一次可能会有成百上千个这样的 rooms/games。

我想知道如何实现它,我愿意使用 Javascript 或 Python 解决方案。

对于 Python,我正在考虑根据需要从线程库中生成尽可能多的计时器,但不确定对性能的影响。所有需要做的就是自动生成一个根本不是资源密集型的选择,并使用 SQLAlchemy 更新 postgres。

我也可以使用 celery 并使用相同的方法为我的消息代理提供延迟任务。

对于 Javascript,类似于 python 的第一个解决方案,我可以根据需要生成尽可能多的 setTimeouts,但问题是所有超时都在主线程上工作,但就像我说的,这些任务不是资源密集型的,我只需要精确到秒。

这些是我到目前为止所讨论的唯一解决方案,我想知道什么是最好的并且对其他解决方案也持开放态度。

Node.js 定时器的实现非常高效。它们的实现(在一篇关于它们如何工作的详细文章中进行了描述)可以轻松处理大量计时器。

它们保存在经过排序的双向链表中,只有下一个要触发的计时器才有与之关联的实际 libuv 系统计时器。当该计时器触发或取消时,列表中的下一个计时器将成为附加到实际 libuv 系统计时器的计时器。当一个新的计时器被设置时,它只是被插入到排序列表中,除非它成为下一个触发的,否则它只是坐在列表中等待轮到下一个。你可以非常有效地拥有数千个这样的东西。

这里有一些关于这些计时器如何工作的资源:

How many concurrent setTimeouts before performance issues?

How does nodejs manage timers internally

第一个参考包含一堆来自实际 nodejs 代码的注释,描述了定时器链表系统如何工作以及它的优化。

第二篇文章对它的组织方式进行了更高级的描述。

还有其他效率,因此当一个计时器到达列表的开头并触发时,然后在调用该回调之后,node.js检查列表的前面是否有任何其他现在就绪的计时器去火也。这将清除与第一个具有相同 "target time" 的所有其他计时器,以及在这些各种其他回调为 运行.

时到期的任何其他计时器

当你有数千个时,如果那里有很多计时器,则将新计时器插入排序链表的时间会稍微长一些,但一旦插入,有多少就变得无关紧要了,因为它只会看着下一个开火。因此,即使有数万个计时器挂起,坐在那里的过程也只是一个系统计时器(代表下一个要触发的计时器事件)和一堆数据。其他未来计时器的所有数据不会让你花任何钱只是坐在那里。

For Javascript, similar to the python's first solution, i could spawn as many setTimeouts as necessary, but the problem is that all timeouts work on the main thread, but like i said, the tasks are not resource intensive and I only need accuracy to the second.

在我看来,nodejs 可以很好地处理您的 setTimeout() 次调用。如果您在 node.js 中遇到问题,那不会是因为计时器的数量,但如果您有更多工作要处理,则必须确定您对问题应用了足够的 CPU一个核心无法处理。您可以使用 nodejs 集群或使用工作线程或其他 nodejs 进程来帮助处理的工作队列来扩展核心使用。 node.js 本身对任何 I/O 相关的事情都非常有效,因此只要您不进行主要的 CPU 密集型计算,单个 node.js 线程就可以处理一个大量事件处理。

请记住,Javascript 计时器不保证准确性。如果您陷入 CPU 的泥潭,一些计时器会比预定时间晚触发,因为它们不是先发制人的。但是,如果您的任务不是 CPU 密集型任务,那么您可能会很好。