trigger quartz persistjobdataafterexecution job example ejemplo disallowconcurrentexecution java concurrency quartz-scheduler

java - quartz - cuarzo: prevención de instancias simultáneas de un trabajo en jobs.xml



scheduler java quartz (7)

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); } ... }

Esto debería ser realmente fácil. Estoy usando Quartz corriendo bajo Apache Tomcat 6.0.18, y tengo un archivo jobs.xml que configura mi trabajo programado que se ejecuta cada minuto.

Lo que me gustaría hacer es si el trabajo aún 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 simultáneas)?

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 de JobExecutionContext ?) Para que yo pueda manejar la concurrencia por mi cuenta? (y detectar si se está ejecutando una instancia anterior)

Actualización: Después de forcejear en los documentos, aquí hay un par de enfoques que estoy considerando, pero o no sé cómo hacerlos funcionar, o hay problemas.

  1. Use StatefulJob . Esto impide el acceso concurrente ... pero no estoy seguro de qué otros efectos secundarios se producirían si lo utilizo, también quiero evitar la siguiente situación:

    Suponga que los tiempos de disparo serían cada minuto, es decir, gatillo # 0 = en el tiempo 0, gatillo # 1 = 60000msec, # 2 = 120000, # 3 = 180000, etc. y el gatillo # 0 en el tiempo 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. Use un TriggerListener para devolver true de vetoJobExecution ().

    Aunque la implementación de la interfaz parece sencilla, tengo que descubrir cómo configurar una instancia de un TriggerListener de manera declarativa. No se pueden encontrar los documentos para el archivo xml .

  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 singletons a través de la palabra clave static en Tomcat / Quartz, no estoy seguro de 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 estado compartido que podría ejecutar su propio TriggerListener.

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


cuando tu trabajo de Quartz se despierte puedes 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 }


¿podría establecer el trabajo como StatefulJob y, por cada activador que cree, configure el MisfireInstruction para que el trabajo no se active 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 disparador.

Gracias: D


La respuesta de dimitrisli no está completa, así que aquí está la mía.

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

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.


Ligera variación de la solución de scaramouche

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; } }

La solución de scaramouche falla cuando hay una sola instancia utilizada para todas las JobExecutions (devolviendo el singleton usando una clase de JobFactory personalizada, en lugar de llamar a newInstance () para cada ejecución)


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. Ver documentos aquí .

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

@DisallowConcurrentExecution public class ColorJob implements Job {


Logré algo similar haciendo que mis clases de trabajo implementaran StatefulJob , lo que garantiza que no se inicien otros trabajos antes de que termine el trabajo en ejecución.

Espero que ayude ;)

PD: Lo implementé usando JBoss ... pero no creo que eso haga la diferencia.