如何在使用集群模块的Node.js应用程序中运行Cron Job?

时间:2015-06-15 11:06:49

标签: node.js cron cluster-computing

我使用node-cron模块在​​Node.js应用程序中调度任务。我还想使用核心集群模块在几个进程中运行应用程序。

在多个流程中运行应用程序最终会在每个流程中执行计划任务(例如,如果任务是发送电子邮件,则会多次发送电子邮件)。

与群集模块一起运行cron作业的最佳做法/可能方法是什么?我应该创建一个单独的进程,只处理cron作业,不接受任何请求。如果是,我该怎样才能以正确的方式做到这一点?

5 个答案:

答案 0 :(得分:12)

如果使用 PM2 , 您可以使用由 PM2 提供的环境变量NODE_APP_INSTANCE,该环境变量需要PM2 2.5或更高版本。

NODE_APP_INSTANCE环境变量可用于确定进程之间的差异,例如,您可能只想在一个进程上运行cronjob,您可以这样做

if(process.env.NODE_APP_INSTANCE == 0) { //schedule your cron job here since this part will be executed for only one cluster }

由于两个进程永远不会有相同的数字。

有关PM2官方文档here的更多信息。

答案 1 :(得分:11)

经过一番研究后,我最终得到了“Distributed locks using Redis”解决方案。 有节点模块:node-redis-warlock

希望这个答案对其他人有用。

<强>更新即可。最小的示例代码:

var Warlock = require('node-redis-warlock'),
    redis = require('redis');

// Establish a redis client
redis = redis.createClient();

// and pass it to warlock
var warlock = new Warlock(redis);

function executeOnce (key, callback) {
    warlock.lock(key, 20000, function(err, unlock){
        if (err) {
            // Something went wrong and we weren't able to set a lock
            return;
        }

        if (typeof unlock === 'function') {
            setTimeout(function() {
                callback(unlock);
            }, 1000);
        }
    });
}

// Executes call back only once
executeOnce('every-three-hours-lock', function(unlock) {
    // Do here any stuff that should be done only once...            
    unlock();          
});

更新2 。更详细的例子:

const CronJob = require('cron').CronJob;
const Warlock = require('node-redis-warlock');
const redis = require('redis').createClient();
const warlock = new Warlock(redis);
const async = require('async');

function executeOnce (key, callback) {
    warlock.lock(key, 20000, function(err, unlock) {
        if (err) {
            // Something went wrong and we weren't able to set a lock
            return;
        }

        if (typeof unlock === 'function') {
            setTimeout(function() {
                callback(unlock);
            }, 1000);
        }
    });
}

function everyMinuteJobTasks (unlock) {
    async.parallel([
        sendEmailNotifications,
        updateSomething,
        // etc...
    ],
    (err) => {
        if (err) {
            logger.error(err);
        }

        unlock();
    });
}

let everyMinuteJob = new CronJob({
    cronTime: '*/1 * * * *',
    onTick: function () {
        executeOnce('every-minute-lock', everyMinuteJobTasks);
    },
    start: true,
    runOnInit: true
});

/* Actual tasks */
let sendEmailNotifications = function(done) {
    // Do stuff here
    // Call done() when finished or call done(err) if error occurred
}

let updateSomething = function(done) {
    // Do stuff here
    // Call done() when finished or call done(err) if error occurred
}

// etc...

答案 2 :(得分:0)

我实际上不喜欢cron-cluster npm插件中也使用过的redis方法,因为我不想在我的机器上运行该redis服务器并对其进行维护。

我想与您讨论这种方法:

专业版:我们不需要使用Redis 缺点:Cron作业始终在同一工人上运行

我仅将消息传递用于此目的,如果将其用于其他用途,则希望传递信息

if (cluster.isMaster) {
    // Count the machine's CPUs
    var cpuCount = require('os').cpus().length;;

    // Create a worker for each CPU
    for (var i = 0; i < cpuCount; i += 1) {
        cluster.fork();
    }

    cluster.on('fork', (worker) => {
        console.log("cluster forking new worker", worker.id);
    });

    // have a mainWorker that does the cron jobs.
    var mainWorkerId = null;

    cluster.on('listening', (worker, address) => {
        console.log("cluster listening new worker", worker.id);
        if(null === mainWorkerId) {
            console.log("Making worker " + worker.id + " to main worker");
            mainWorkerId = worker.id;
        worker.send({order: "startCron"});
        }
    });

    // Listen for dying workers if the mainWorker dies, make a new mainWorker
    cluster.on('exit', function (worker, code, signal) {
        console.log('Worker %d died :(', worker.id);

        if(worker.id === mainWorkerId) {
            console.log("Main Worker is dead...");
            mainWorkerId = null;
        }

        console.trace("I am here");
        console.log(worker);
        console.log(code);
        console.log(signal);
        cluster.fork();

    });
// Code to run if we're in a worker process
} else {

    // other code like setup app and stuff

    var doCron = function() {
        // setup cron jobs...
    }

    // Receive messages from the master process.
    process.on('message', function(msg) {
        console.log('Worker ' + process.pid + ' received message from master.', message);
        if(message.order == "startCron") {
            doCron();
        }
    });
}

答案 3 :(得分:0)

我也对集群模块有问题,最后我找到了解决问题的示例方法。

让主集群执行cronJob。

我的项目使用Kue管理作业。当cronJob运行时,我会得到一份工作清单。

index.js

global.cluster = require('cluster');

if (cluster.isMaster) {
  const cpuCount = require('os').cpus().length;
  for (let i = 0; i < cpuCount; i += 1) {
    cluster.fork();
  }
} else {
  // start your express server here
  require('./server')
}

cluster.on('exit', worker => {
  logger.warn('Worker %d died :(', worker.id);
  cluster.fork();
});

cron.js

const cron = require('cron').CronJob;

const job = new cron('* * * * *', async () => {
  if (cluster.isMaster) {
    console.log('cron trigger');
  }
});

job.start();

希望获得帮助。

答案 4 :(得分:0)

我认为您可以使用节点集群模块,并且可以在其中编写仅在主集群中运行的代码

const cluster = require('cluster');

if (cluster.isMaster) {
     // Write your code which you want to execute in the master cluster only
}

这是处理群集的一种节点方式,当然,您可以使用pm2之类的任何工具来处理此问题。