2010-12-10 14 views
16

Digamos que tengo un disparador configurado de esta manera:cuarzo reintento cuando el fracaso

<bean id="updateInsBBTrigger"   
    class="org.springframework.scheduling.quartz.CronTriggerBean"> 
    <property name="jobDetail" ref="updateInsBBJobDetail"/> 
    <!-- run every morning at 5 AM --> 
    <property name="cronExpression" value="0 0 5 * * ?"/> 
</bean> 

El disparador tiene que conectar con otra aplicación y si hay algún problema (como un fallo de conexión) que debería volver a intentar la tarea hasta cinco veces cada 10 minutos o hasta que tenga éxito. ¿Hay alguna manera de configurar el disparador para que funcione de esta manera?

Respuesta

16

Fuente: Automatically Retry Failed Jobs in Quartz

Si usted quiere tener un trabajo que sigue tratando una y otra vez hasta que tenga éxito, todo lo que tiene que hacer es lanzar una JobExecutionException con una bandera para decirle al programador para dispararla de nuevo cuando falla. El siguiente código muestra cómo:

class MyJob implements Job { 

    public MyJob() { 
    } 

    public void execute(JobExecutionContext context) throws JobExecutionException { 

     try{ 
      //connect to other application etc 
     } 
     catch(Exception e){ 

      Thread.sleep(600000); //sleep for 10 mins 

      JobExecutionException e2 = new JobExecutionException(e); 
      //fire it again 
      e2.setRefireImmediately(true); 
      throw e2; 
     } 
    } 
} 

Se vuelve un poco más complicado si desea volver a intentar una cierta cantidad de veces. Debe utilizar StatefulJob y mantener retryCounter en su JobDataMap, que aumentará si el trabajo falla. Si el contador excede la cantidad máxima de reintentos, puede desactivar el trabajo si lo desea.

class MyJob implements StatefulJob { 

    public MyJob() { 
    } 

    public void execute(JobExecutionContext context) throws JobExecutionException { 
     JobDataMap dataMap = context.getJobDetail().getJobDataMap(); 
     int count = dataMap.getIntValue("count"); 

     // allow 5 retries 
     if(count >= 5){ 
      JobExecutionException e = new JobExecutionException("Retries exceeded"); 
      //make sure it doesn't run again 
      e.setUnscheduleAllTriggers(true); 
      throw e; 
     } 


     try{ 
      //connect to other application etc 

      //reset counter back to 0 
      dataMap.putAsString("count", 0); 
     } 
     catch(Exception e){ 
      count++; 
      dataMap.putAsString("count", count); 
      JobExecutionException e2 = new JobExecutionException(e); 

      Thread.sleep(600000); //sleep for 10 mins 

      //fire it again 
      e2.setRefireImmediately(true); 
      throw e2; 
     } 
    } 
} 
+0

Gracias. Esto es lo que estaba buscando. – Averroes

+43

-1, no recomiendo este enfoque: bloqueará uno de los hilos de trabajo de Quartz durante 10 minutos.El camino correcto sería facilitar la funcionalidad existente de Quartz, decirle de alguna manera que vuelva a ejecutar el mismo trabajo después de 10 minutos, después de todo, esto es para lo que está hecho. Si vamos a ejecutar algún código y dormir, no tiene sentido usar Quartz en primer lugar. –

+1

complemento que en Quartz 2.0 (para .net al menos). StatefulJob se reemplaza por 'PersistJobDataAfterExecutionAttribute' http://quartznet.sourceforge.net/apidoc/2.0/html/html/babe3560-218c-38de-031a-7fe1fdd569d2.htm – ossek

7

que sugeriría una mayor flexibilidad y capacidad de configuración a una mejor tienda en su base de datos dos compensaciones: la repeatOffset que le dirá después de cuánto tiempo el trabajo debe ser juzgado y el trialPeriodOffset que mantendrá la información de la ventana de tiempo en que el trabajo es permitido reprogramarse. A continuación, puede recuperar estos dos parámetros como (supongo que está utilizando la primavera):

String repeatOffset = yourDBUtilsDao.getConfigParameter(..); 
String trialPeriodOffset = yourDBUtilsDao.getConfigParameter(..); 

Entonces, en lugar de la tarea de recordar el contador tendrá que recordar la initalAttempt:

Long initialAttempt = null; 
initialAttempt = (Long) existingJobDetail.getJobDataMap().get("firstAttempt"); 

y realizar el algo como lo siguiente comprobación:

long allowedThreshold = initialAttempt + Long.parseLong(trialPeriodOffset); 
     if (System.currentTimeMillis() > allowedThreshold) { 
      //We've tried enough, time to give up 
      log.warn("The job is not going to be rescheduled since it has reached its trial period threshold"); 
      sched.deleteJob(jobName, jobGroup); 
      return YourResultEnumHere.HAS_REACHED_THE_RESCHEDULING_LIMIT; 
     } 

sería una buena idea para crear una enumeración para el resultado del intento que se devuelve de nuevo al flujo de trabajo de su núcleo aplicación como la anterior.

luego construir el tiempo de reprogramación:

Date startTime = null; 
startTime = new Date(System.currentTimeMillis() + Long.parseLong(repeatOffset)); 

String triggerName = "Trigger_" + jobName; 
String triggerGroup = "Trigger_" + jobGroup; 

Trigger retrievedTrigger = sched.getTrigger(triggerName, triggerGroup); 
if (!(retrievedTrigger instanceof SimpleTrigger)) { 
      log.error("While rescheduling the Quartz Job retrieved was not of SimpleTrigger type as expected"); 
      return YourResultEnumHere.ERROR; 
} 

     ((SimpleTrigger) retrievedTrigger).setStartTime(startTime); 
     sched.rescheduleJob(triggerName, triggerGroup, retrievedTrigger); 
     return YourResultEnumHere.RESCHEDULED; 
2

Yo recomendaría una aplicación como ésta para recuperar el trabajo después de una falla:

final JobDataMap jobDataMap = jobCtx.getJobDetail().getJobDataMap(); 
// the keys doesn't exist on first retry 
final int retries = jobDataMap.containsKey(COUNT_MAP_KEY) ? jobDataMap.getIntValue(COUNT_MAP_KEY) : 0; 

// to stop after awhile 
if (retries < MAX_RETRIES) { 
    log.warn("Retry job " + jobCtx.getJobDetail()); 

    // increment the number of retries 
    jobDataMap.put(COUNT_MAP_KEY, retries + 1); 

    final JobDetail job = jobCtx 
     .getJobDetail() 
     .getJobBuilder() 
     // to track the number of retries 
     .withIdentity(jobCtx.getJobDetail().getKey().getName() + " - " + retries, "FailingJobsGroup") 
     .usingJobData(jobDataMap) 
     .build(); 

    final OperableTrigger trigger = (OperableTrigger) TriggerBuilder 
     .newTrigger() 
     .forJob(job) 
     // trying to reduce back pressure, you can use another algorithm 
     .startAt(new Date(jobCtx.getFireTime().getTime() + (retries*100))) 
     .build(); 

    try { 
    // schedule another job to avoid blocking threads 
    jobCtx.getScheduler().scheduleJob(job, trigger); 
    } catch (SchedulerException e) { 
    log.error("Error creating job"); 
    throw new JobExecutionException(e); 
    } 
} 

¿Por qué?

  1. No va a bloquear cuarzo Trabajadores
  2. Se evitará la contrapresión. Con setRefireImmediately el trabajo se disparará inmediatamente y podría generar problemas de contrapresión
Cuestiones relacionadas