数据库支持的工作队列

时间:2011-04-09 05:08:20

标签: javascript architecture node.js scale beanstalkd

我的情况......

我有一组 worker ,它们被安排定期运行,每个都以不同的时间间隔运行,并希望找到一个好的实现来管理它们的执行。

示例:假设我有一个工人去商店,每周给我买一次牛奶。我想将这个作业及其配置存储在mysql表中。但是,轮询表(每秒?)似乎是一个真的坏主意,并且看看哪些作业已准备好放入执行管道。

我的所有工作人员都是用javascript编写的,所以我使用node.js执行,beanstalkd作为管道。

如果正在异步创建新作业(即调度工作程序在给定时间运行)并且我需要持久存储作业结果和配置,那么如何避免轮询表?

谢谢!

2 个答案:

答案 0 :(得分:2)

我同意这看起来不够优雅,但考虑到计算机工作的方式某些事情 *某处*将需要轮询某种才能弄明白哪些工作要执行。那么,让我们来看看你的一些选择:

  1. 轮询数据库表。这根本不是一个坏主意 - 如果你将工作存储在MySQL中,它可能是最简单的选择。每秒一个查询的速率是没有的 - 尝试一下,你会注意到你的系统甚至没有感觉到它。

    一些想法可帮助您将此扩展到每秒数百个查询,或者只是降低系统资源要求:

    • 创建第二个表'job_pending',您可以在下一个X秒/分钟/小时内放置需要执行的作业。
    • 仅在较长时间内对所有作业的大表运行查询,然后填充您每隔一段时间查询的小表。
    • 删除从小表中执行的作业,以使其保持较小。
    • 在'execute_time'(或任何你称之为)列上使用索引。
  2. 如果你必须进一步扩展,请将主作业表保留在数据库中,并使用我建议的第二个较小的表,只需将该表放入RAM:作为数据库引擎中的内存表,或者在你的程序中的某种队列中。如果你有的话,以极短的间隔查询队列 - 这将需要一些极端的用例来引起任何性能问题。

    此选项的主要问题是您必须跟踪内存中但未执行的作业,例如由于系统崩溃 - 为您编写更多代码......

  3. 为一堆作业中的每一个创建一个线程(比如,下一分钟需要执行的所有作业),并调用thread.sleep(millis_until_execution_time)(或者其他什么,我不熟悉的node.js)。

    此选项与no具有相同的问题。 2 - 您必须跟踪作业执行以进行崩溃恢复。它也是最浪费的imo - 每个睡眠作业线程仍然需要系统资源。

  4. 当然可能还有其他选择 - 我希望其他人回答更多的想法。

    只是意识到每秒轮询数据库并不是一个坏主意。这是imo最直接的方式(记住KISS),按照这个速度,你不应该有性能问题,所以要避免过早的优化。

答案 1 :(得分:1)

为什么在node.js中没有保存到数据库的Job对象。

var Job = {
   id: long,
   task: String,
   configuration: JSON,
   dueDate: Date,
   finished: bit
};

我建议你只将id存储在RAM中,并将所有其他Job数据保留在数据库中。当你的超时功能最终运行时,它只需要知道.id来获取其他数据。

var job = createJob(...); // create from async data somewhere.
job.save(); // save the job.
var id = job.id // only store the id in RAM
// ask the job to be run in the future.
setTimeout(Date.now - job.dueDate, function() {
    // load the job when you want to run it
    db.load(id, function(job) {
        // run it.
        run(job);
        // mark as finished
        job.finished = true;
        // save your finished = true state
        job.save();
    });
});
// remove job from RAM now.
job = null;

如果服务器崩溃了,那么查询所有[finished=false]的作业,将它们加载到RAM中并再次启动setTimeouts。

如果出现任何问题,您应该能够像这样彻底重启:

db.find("job", { finished: false }, function(jobs) {
    each(jobs, function(job) {
         var id = job.id;
         setTimeout(Date.now - job.dueDate, function() {
             // load the job when you want to run it
             db.load(id, function(job) {
                 // run it.
                 run(job);
                 // mark as finished
                 job.finished = true;
                 // save your finished = true state
                 job.save();
             });
         });
         job = null;
    });
});