[英]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 运行时,我会得到一份工作列表。
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.