繁体   English   中英

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

[英]How to run Cron Job in Node.js application that uses cluster module?

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

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

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

如果使用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 官方文档的信息在这里

经过一番研究,我最终得到了“使用 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...

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

我想和你讨论这个方法:

优点:我们不需要使用 redis 缺点:cron 作业总是在同一个 worker 上运行

我只为此使用消息传递,如果你将它用于其他事情,你想要传递的信息

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();
        }
    });
}

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

让主集群执行 cronJob。

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

索引.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();
});

定时任务

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

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

job.start();

希望这有帮助。

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

const cluster = require('cluster');

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

这是node处理集群的方式,当然你可以用pm2之类的任何工具来处理。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM