Подтвердить что ты не робот

Как запустить задание Cron в приложении Node.js, использующем кластерный модуль?

Я использую node-cron модуль для планирования задач в приложении Node.js. Я также хочу запустить приложение в нескольких процессах с использованием основного кластерного модуля.

Запуск приложения в нескольких процессах завершается выполнением запланированных задач в каждом процессе (например, если задача заключалась в отправке электронной почты, письмо будет отправляться несколько раз).

Каковы наилучшие методы/возможные способы выполнения задания cron вместе с кластерным модулем? Должен ли я создать отдельный процесс, который будет обрабатывать только задание cron и не принимать какие-либо запросы. Если да, то как я могу сделать это правильно?

4b9b3361

Ответ 1

После некоторого исследования я закончил с "" Распределенные блокировки с использованием Redis". Для этого есть модуль node: node-redis-warlock.

Надеюсь, что этот ответ будет полезен кому-то другому.

UPDATE. Минимальный пример кода:

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

Если используются 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 здесь.