如何在使用集群模块的 Node.js 应用程序中 运行 Cron Job?
How to run Cron Job in Node.js application that uses cluster module?
我正在使用 node-cron 模块在 Node.js 应用程序中安排任务。我还希望 运行 在多个进程中使用核心集群模块的应用程序。
运行 多个进程中的应用程序最终会在每个进程中执行计划任务(例如,如果任务是发送电子邮件,则电子邮件将被发送多次)。
practices/possible运行宁定时作业和集群模块的最佳practices/possible方法是什么?我是否应该创建一些单独的进程来处理 cron 作业并且不接受任何请求。如果是,我怎样才能以正确的方式做到这一点?
经过一番研究,我最终得到了“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...
如果正在使用 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。
我实际上不喜欢 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();
}
});
}
我也有集群模块的问题,最后我找到了解决问题的示例方法。
Let master cluster execute 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();
希望对您有所帮助。
我认为你可以使用节点集群模块,在那里你可以只在主集群中将代码写入 运行
const cluster = require('cluster');
if (cluster.isMaster) {
// Write your code which you want to execute in the master cluster only
}
这是node的方式来处理cluster,当然你也可以用pm2之类的工具来处理。
我正在使用 node-cron 模块在 Node.js 应用程序中安排任务。我还希望 运行 在多个进程中使用核心集群模块的应用程序。
运行 多个进程中的应用程序最终会在每个进程中执行计划任务(例如,如果任务是发送电子邮件,则电子邮件将被发送多次)。
practices/possible运行宁定时作业和集群模块的最佳practices/possible方法是什么?我是否应该创建一些单独的进程来处理 cron 作业并且不接受任何请求。如果是,我怎样才能以正确的方式做到这一点?
经过一番研究,我最终得到了“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...
如果正在使用 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。
我实际上不喜欢 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();
}
});
}
我也有集群模块的问题,最后我找到了解决问题的示例方法。
Let master cluster execute 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();
希望对您有所帮助。
我认为你可以使用节点集群模块,在那里你可以只在主集群中将代码写入 运行
const cluster = require('cluster');
if (cluster.isMaster) {
// Write your code which you want to execute in the master cluster only
}
这是node的方式来处理cluster,当然你也可以用pm2之类的工具来处理。