Как настроить задачу CRON для запуска только один раз для набора экземпляров? - PullRequest
5 голосов
/ 03 ноября 2011

Мы размещаем наш сайт в AWS. В настоящее время у нас есть 3 экземпляра EC2 в одном кластере с использованием балансировщика нагрузки AWS.

На серверах установлены linux, apache, java, mysql и tomcat 6.0.

Мы принимаем решение о том, как настроить задачу для запуска каждый час. Очевидное место для этого - код Java, но есть одна проблема.

Проблема в том, что, поскольку у нас есть 3 экземпляра в кластере (все они идентичны), задача будет выполняться 3 раза в час, а не один раз в час, один раз для экземпляра.

У меня есть несколько идей, чтобы преодолеть это, но я надеялся, что есть лучший, возможно, отраслевой стандарт, как управлять этим.

Одна из идей - сохранить в БД, что она уже запущена. Задание увидит, что оно уже запущено сегодня или нет. Хотя я вижу там ошибки.

Другая идея состояла в том, чтобы использовать cron, установленный на одном из экземпляров в собственной ОС, вне кода в Tomcat. Это будет использовать wget для вызова веб-страницы, которая вызывает метод Java. Поскольку это вызвало бы только один из экземпляров, оно должно выполняться только один раз.

Оба способа кажутся взломами и подвержены ошибкам. Есть ли реальный способ сделать это?

Ответы [ 2 ]

4 голосов
/ 11 июня 2015

Хотя есть принятый ответ, есть несколько простых, доморощенных решений, в которых нет ошибок, как вы описали выше.Решение wget хорошо работает для обеспечения того, чтобы код выполнялся на одном сервере, но добавляет проблемы безопасности (вы должны защитить URL с помощью общего закрытого ключа доступа), и, как указал @sourcedelica, также вопрос о том, какой сервер должен затем вызыватьЗадача cron.

Я склоняюсь к решению, которое работает независимо от количества систем, которые у вас есть, а также не требует разных конфигураций cron для разных систем.

Предположениечто в будущем вы можете добавить новые машины, и ваш основной сервер (например, сконфигурированный для запуска вашей задачи cron) может умереть или быть остановленным.

Разработанное мной решение использует блокировки базы данных кластера, которыеможет быть сделано с двумя простыми таблицами:

CREATE TABLE `Server` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `uname` varchar(32) NOT NULL,
    `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `alive` timestamp NULL DEFAULT NULL,
    PRIMARY KEY (`id`)
);

CREATE TABLE `Lock` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `code` varchar(128) NOT NULL,
    `pid` int(10) unsigned DEFAULT NULL,
    `server` int(10) unsigned DEFAULT NULL,
    `locked` timestamp NULL DEFAULT NULL,
    `used` timestamp NULL DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `code` (`code`)
);

Каждая система имеет уникальный uname и регистрирует запись, если она не существует;обновляя alive каждый раз.

Чтобы получить замок:

SELECT * FROM Lock WHERE code='cron-cluster';

Если его не существует,

INSERT INTO `Lock` ...

Как только вы получите Lock с id из 32.Если server и pid оба равны NULL, задайте им для моего сервера id и текущий идентификатор процесса, используя атомарную природу базы данных, чтобы обеспечить только один.

UPDATE Lock SET server=1,pid=4233 WHERE id=32 AND server IS NULL and pid IS NULL;

Затем вы делаетевыберите еще раз, чтобы увидеть, действительно ли вы его приобрели (при условии, что n разных машин пытаются получить блокировку одновременно):

SELECT COUNT(id) FROM Lock WHERE code='cron-cluster' AND server=1 AND pid=4233;

Если результат равен 1, вы получаете блокировку, 0означает, что это сделал другой процесс.

Последнее, что нужно сделать, это очистить каждый сервер от мертвых блокировок и мертвых серверов;каждый сервер отвечает за проверку того, что активный процесс запущен для каждого заблокированного Lock, а когда Server не обновляется как alive по истечении определенного времени, удалите все блокировки, связанные с этим сервером и его Serverзапись.

Я добавил другие свойства сервера в таблицу Server, чтобы разрешить мониторинг дискового пространства, процессора и т. д.

Хотя он не такой мощный, как кластеризация Quartz, он решает вашу проблему.

3 голосов
/ 03 ноября 2011

Я использовал решение cron / wget, и это действительно разумный способ решения проблемы.Ваши системные администраторы оценят возможность управлять им.

Другое решение - использовать системное свойство JVM, чтобы указать, какой из ваших экземпляров является тем, который выполняет задания.Например: -DschedulerEnabled=true.Установите этот флаг только на одном из экземпляров, и код планирования заданий будет выполняться только в том случае, если этот флаг установлен.

Наконец, Quartz поддерживает решение на основе БД с помощью функции Clustering .Преимущество этого в том, что это действительно решение HA.С другими решениями, если машина, выполняющая роль планировщика заданий, выходит из строя, вам нужно вручную переключиться на другую машину.

...