【问题标题】:Database Backed Work Queue数据库支持的工作队列
【发布时间】:2011-08-01 23:03:15
【问题描述】:

我的情况……

我有一组 workers 计划定期运行,每个运行时间不同,并且希望找到一个好的实现来管理它们的执行。

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

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

如果新作业(即安排工作人员在给定时间运行)是异步创建的,我需要持久存储作业结果和配置,如何避免轮询表?

谢谢!

【问题讨论】:

    标签: javascript architecture node.js scale beanstalkd


    【解决方案1】:

    我同意这看起来不优雅,但考虑到计算机的工作方式某事 *somewhere* 将不得不对某种 进行轮询才能弄清楚什么时候执行哪些作业。所以,让我们来看看你的一些选择:

    1. 轮询数据库表。这根本不是一个坏主意——如果您将作业存储在 MySQL 中,这可能是最简单的选择。每秒一个查询的速度没什么——试一试,你会发现你的系统甚至感觉不到它。

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

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

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

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

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

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

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

    【讨论】:

    • 我同意 KISS 说投票没问题。 OTOH,通知者/观察者没有任何形式的轮询。当需要发生某些事情时,会通知所有订阅者。唯一的网络流量是keepalive 或heartbeats,在某些守护进程/系统/软件中可能发生的次数越少越好。
    【解决方案2】:

    为什么不在 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;
        });
    });
    

    【讨论】:

      猜你喜欢
      • 2016-01-24
      • 1970-01-01
      • 2016-03-17
      • 1970-01-01
      • 2012-06-12
      • 1970-01-01
      • 2012-07-23
      • 1970-01-01
      • 2013-10-03
      相关资源
      最近更新 更多