2011-11-02 23 views
5

Estamos alojando nuestro sitio web en AWS. Actualmente tenemos 3 instancias EC2 en un clúster, utilizando el equilibrador de carga AWS.¿Cómo configurar una tarea CRON para que se ejecute solo una vez por conjunto de instancias?

Los servidores tienen linux, apache, java, mysql y tomcat 6.0.

Estamos tomando una decisión sobre cómo configurar una tarea para que se ejecute cada hora. El lugar obvio para hacer esto es en el código de Java, pero hay un problema.

El problema es que, dado que tenemos 3 instancias en el clúster (todas son idénticas), la tarea se ejecutará 3 veces por hora, en lugar de una vez por hora, una vez por instancia.

Tengo algunas ideas para superar esto, pero esperaba que haya una mejor, posiblemente un estándar de la industria, sobre cómo gestionar esto.

Una idea es almacenar en el DB que ya se ejecutó. La tarea verá que ya se ejecutó hoy o no. Aunque veo bichos allí.

La otra idea era utilizar cron instalado en una de las instancias en el sistema operativo nativo, fuera del código en Tomcat. Esto usaría wget para llamar a una página web que llama a un método de Java. Como eso solo llamaría a una de las instancias, solo debería ejecutarse una vez.

Ambas maneras parecen piratas y propensas a errores. ¿Hay una manera real de hacer esto?

Respuesta

3

He utilizado la solución cron/wget y en realidad es una forma razonable de resolver el problema. Los administradores de su sistema apreciarán poder controlarlo.

Otra solución es utilizar una propiedad del sistema JVM para indicar cuál de sus instancias es la que ejecuta los trabajos. Por ejemplo: -DschedulerEnabled=true. Establezca solo ese indicador en una de las instancias y haga que el código de programación de trabajos solo se ejecute si ese indicador está establecido.

Finalmente, Quartz admite su solución basada en DB con su característica Clustering. La ventaja de esto es que es realmente una solución HA. Con las otras soluciones, si la máquina que actúa como programador de tareas se cae, tiene que pasar manualmente a otra máquina.

+0

Además, acaba de encontrar rcron que también puede resolverlo: https://code.google.com/p/rcron/ – razzed

3

Si bien hay una respuesta aceptada, hay algunas soluciones simples, de cosecha propia que no tienen los errores como se describe anteriormente. La solución wget funciona bien para garantizar que un único servidor ejecute el código, pero agrega problemas de seguridad (debe proteger la URL con una clave de acceso privada compartida), y como @sourcedelica también señaló el problema de qué servidor debería invocar realmente la tarea cron.

Tiendo a ir por la solución que funciona independientemente de la cantidad de sistemas que tenga, y también no requiere configuraciones cron diferentes para diferentes sistemas.

Se supone que, en el futuro, puede agregar máquinas nuevas y su servidor primario (el configurado para ejecutar su tarea cron, por ejemplo) puede morir o terminar.

Una solución que he desarrollado utiliza bloqueos de base de datos de clúster que se puede hacer con un dos tablas simples:

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`) 
); 

Cada sistema tiene una única uname, y registra un registro si no existe; actualizando alive cada vez.

Para adquirir un bloqueo:

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

Si no existe,

INSERT INTO `Lock` ... 

vez que tenga su Lock con id de 32. Si server y pid son ambas NULL, los puso a mi servidor id y la identificación del proceso actual, usando la naturaleza atómica de la base de datos para asegurar solo una.

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

Entonces usted un selecto de nuevo para ver si realmente lo adquirió (suponiendo que n diferentes máquinas están tratando de adquirir el bloqueo al mismo tiempo):

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

Si el resultado es 1 , has adquirido el bloqueo, 0 significa que otro proceso lo hizo.

Lo último que se necesita es que cada servidor limpie bloqueos muertos y servidores muertos; cada servidor es responsable de comprobar que se está ejecutando un proceso activo para cada Lock bloqueado, y cuando un Server no se actualiza como alive después de un tiempo de espera determinado, elimine todos los bloqueos asociados con ese servidor y su registro Server.

añadí otras propiedades del servidor a la mesa Server para permitir el monitoreo de espacio en disco, CPU, etc.

Aunque no es tan poderoso como el cuarzo agrupación, que resuelve su problema.

+0

Me encanta su trabajo, este me dio la idea que me faltaba para resolverlo. Fui un poco más simple, usando MySQL el cron se puede ejecutar en cualquier intervalo frecuente, fui con 4 horas: '" ACTUALIZACIÓN bloqueo SET servidor = 1, lock_date = NOW() WHERE lock_date shazbot

Cuestiones relacionadas