我使用node-cron模块在Node.js应用程序中调度任务。我还想使用核心集群模块在几个进程中运行应用程序。
在多个流程中运行应用程序最终会在每个流程中执行计划任务(例如,如果任务是发送电子邮件,则会多次发送电子邮件)。
与群集模块一起运行cron作业的最佳做法/可能方法是什么?我应该创建一个单独的进程,只处理cron作业,不接受任何请求。如果是,我该怎样才能以正确的方式做到这一点?
答案 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运行时,我会得到一份工作清单。
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();
希望获得帮助。
答案 4 :(得分:0)
我认为您可以使用节点集群模块,并且可以在其中编写仅在主集群中运行的代码
const cluster = require('cluster');
if (cluster.isMaster) {
// Write your code which you want to execute in the master cluster only
}
这是处理群集的一种节点方式,当然,您可以使用pm2之类的任何工具来处理此问题。