2010-04-20 19 views
39

Esto debería ser realmente fácil. Estoy usando Quartz corriendo bajo Apache Tomcat 6.0.18, y tengo un jobs.xml file que configura mi trabajo programado que se ejecuta cada minuto.cuarzo: prevención de instancias simultáneas de un trabajo en jobs.xml

Lo que me gustaría hacer es si el trabajo todavía se está ejecutando cuando llegue el siguiente momento de activación, no quiero comenzar un nuevo trabajo, así que puedo dejar que la instancia anterior se complete.

¿Hay alguna manera de especificar esto en jobs.xml (evitar instancias concurrentes)?

Si no, ¿hay alguna manera de compartir el acceso a un singleton en memoria dentro de la implementación de Job de mi aplicación (esto es a través del JobExecutionContext?) ¿Así que puedo manejar la concurrencia por mi cuenta? (Y detectar si una instancia anterior se está ejecutando)


actualización: Después de forcejeo en torno a la documentación, aquí hay un par de enfoques que estoy considerando, pero o bien no saben cómo llegar a trabajar, o hay problemas

  1. Use StatefulJob. Esto impide el acceso simultáneo ... pero no estoy seguro de qué otros efectos secundarios podrían ocurrir si lo uso, también quiero evitar la siguiente situación:

    Supongamos que los tiempos de disparo serían cada minuto, es decir, disparador # 0 = en el tiempo 0, disparador # 1 = 60000msec, # 2 = 120000, # 3 = 180000, etc. y el gatillo # 0 en el momento 0 dispara mi trabajo que toma 130000msec. Con un trabajo simple, esto ejecutaría los disparadores # 1 y # 2 mientras el disparador de trabajo # 0 aún se está ejecutando. Con StatefulJob, esto ejecutaría los disparadores # 1 y # 2 en orden, inmediatamente después de que el # 0 termine en 130000. No quiero eso, quiero que los # 1 y # 2 no se ejecuten y el siguiente disparador que ejecuta un trabajo debería tener lugar en # 3 (180000msec). Así que todavía tengo que hacer algo más con StatefulJob para que funcione como yo quiero, así que no veo mucha ventaja en su uso.

  2. Utilice un TriggerListener para devolver true de vetoJobExecution().

    Aunque la implementación de la interfaz parece sencilla, tengo que averiguar cómo configurar una instancia de un TriggerListener de manera declarativa. Can't find the docs for the xml file.

  3. Use un objeto static compartido seguro para subprocesos (por ejemplo, un semáforo o lo que sea) propiedad de mi clase que implementa el trabajo.

    no me gusta la idea de usar únicos a través de la palabra clave static bajo Tomcat/cuarzo, no estoy seguro si hay efectos secundarios. También realmente no quiero que sean verdaderos singletons, solo algo que está asociado con una definición de trabajo en particular.

  4. Implemente mi propio Trigger que extiende SimpleTrigger y contiene el estado compartido que podría ejecutar su propio TriggerListener.

    De nuevo, no sé cómo configurar el archivo XML para usar este disparador en lugar del estándar <trigger><simple>...</simple></trigger>.

+8

en las nuevas versiones de cuarzo, que marca el trabajo de implementación de clase con @DisallowConcurrentExecution se asegura de que sólo una clase corre en cualquier momento ... – sbrattla

Respuesta

17

cuando el trabajo de cuarzo despierta que puede hacer:

JobDetail existingJobDetail = sched.getJobDetail(jobName, jobGroup); 
    if (existingJobDetail != null) { 
     List<JobExecutionContext> currentlyExecutingJobs = (List<JobExecutionContext>) sched.getCurrentlyExecutingJobs(); 
     for (JobExecutionContext jec : currentlyExecutingJobs) { 
      if(existingJobDetail.equals(jec.getJobDetail())) { 
       //String message = jobName + " is already running."; 
       //log.info(message); 
       //throw new JobExecutionException(message,false); 
      } 
     } 
     //sched.deleteJob(jobName, jobGroup); if you want to delete the scheduled but not-currently-running job 
    } 
+1

Parece interesante ... debe consultar con el JobExecutionContext existente; de ​​lo contrario, siempre obtendrá la excepción. También hay una excepción lanzada por sched.getCurrentlyExecutingJobs() que debe capturarse. –

5

que he logrado algo similar a hacer mis clases de trabajo implementan StatefulJob, que asegura que no hay otros puestos de trabajo comienza antes termina el trabajo actual en ejecución.

Espero que ayude;)

PD: He implementado usando JBoss ... pero no creo que hay alguna diferencia.

+0

ayuda un poco, pero no realmente, ver mi comentario # 1 anterior. –

+1

@younghobbit, ver la respuesta actualizada – Ramses

4

¿podría establecer el trabajo como StatefulJob, y para cada uno de gatillo que crear un conjunto de MisfireInstruction para el trabajo que se dispara si se pierde? No estoy seguro de qué tipo de trabajo está utilizando, pero tendrá que investigar un poco sobre las instrucciones de fallo de funcionamiento que están disponibles para su tipo de activador.

Gracias, D respuesta

20

de dimitrisli no es completa por lo que es mío aquí.

Cuando Quartz Job se despierta, le devuelve JobExecutionContext. Supongo que quiere omitir trabajos con el mismo desencadenador.

List<JobExecutionContext> jobs = jobExecutionContext.getScheduler().getCurrentlyExecutingJobs(); 
      for (JobExecutionContext job : jobs) { 
       if (job.getTrigger().equals(jobExecutionContext.getTrigger()) && !job.getJobInstance().equals(this)) { 
        logger.info("There's another instance running, so leaving" + this); 
        return; 
       } 

      } 

Obtenemos contextos de trabajo actuales y verificamos si hay una instancia de trabajo anterior con el mismo activador. Si este es el caso, saltemos con return.

56

Hay otra solución más simple. El trabajo puede recibir una anotación de DisallowConcurrentExecution que impide que se ejecuten varias instancias simultáneas. Consulte los documentos here.

El enlace sigue rompiéndose, así que aquí está la muestra relevante.

@DisallowConcurrentExecution 
public class ColorJob implements Job { 
+7

El uso de la anotación DisallowConcurrentExecution pone en cola la instancia actual para su ejecución hasta que finalice la instancia anterior. ¿Hay alguna forma de eliminar la instancia actual de la ejecución si ya se está ejecutando una instancia de Job? –

+2

Tenga en cuenta que la restricción solo se aplica a trabajos con el mismo JobKey (que generalmente es el caso). Los trabajos con diferentes JobKeys aún se pueden ejecutar simultáneamente incluso si se implementan con la misma clase. – stenix

1

ligera variación a la solución de Scaramouche. solución

List<JobExecutionContext> jobs = jobExecutionContext.getScheduler().getCurrentlyExecutingJobs(); 
for (JobExecutionContext job : jobs) { 
    if (job.getTrigger().equals(jobExecutionContext.getTrigger()) && !job.getFireInstanceId().equals(jobExecutionContext.getFireInstanceId()) { 
     logger.info("There's another instance running, so leaving" + this); 
     return; 
    } 

} 

de scaramouche falla cuando hay una sola instancia utilizado para todos los JobExecutions (volviendo el singleton utilizando una clase JobFactory costumbre, en lugar de llamar newInstance() para cada ejecución)

1

Si está utilizando org.springframework.scheduling.quartz.QuartzJobBean:

protected void executeInternal(JobExecutionContext context) throws JobExecutionException { 
    try { 
     Scheduler scheduler = context.getScheduler(); 
     List<JobExecutionContext> jobs = scheduler.getCurrentlyExecutingJobs(); 
     for (JobExecutionContext job : jobs) { 
      if (job.getTrigger().equals(context.getTrigger()) && job.getJobDetail() != context.getJobDetail()) { 
       LOG.warn("Ignored!"); 
       return; 
      } 
     } 
     ... 
    } catch (SchedulerException e) { 
     LOG.error("What a luck! :'(", e); 
    } 
    ... 
} 
Cuestiones relacionadas