web-dev-qa-db-fra.com

quartz: prévention des instances simultanées d'un travail dans jobs.xml

Cela devrait être vraiment facile. J'utilise Quartz sous Apache Tomcat 6.0.18 et j'ai fichier jobs.xml qui configure mon travail planifié qui s'exécute toutes les minutes.

Ce que je voudrais faire, c'est que si le travail est toujours en cours d'exécution lors du prochain déclenchement, je ne veux pas démarrer un nouveau travail, donc je peux laisser l'ancienne instance se terminer.

Existe-t-il un moyen de spécifier cela dans jobs.xml (empêcher les instances simultanées)?

Sinon, existe-t-il un moyen de partager l'accès à un singleton en mémoire dans l'implémentation Job de mon application (est-ce par le biais de JobExecutionContext ?) Afin que je puisse gérer la concurrence moi même? (et détecter si une instance précédente est en cours d'exécution)


mise à jour: Après avoir pataugé dans les documents, voici quelques approches que j'envisage, mais soit je ne sais pas comment les faire fonctionner, soit Il y a des problemes.

  1. Utilisez StatefulJob . Cela empêche l'accès simultané ... mais je ne sais pas quels autres effets secondaires se produiraient si je l'utilisais, je veux également éviter la situation suivante:

    Supposons que les temps de déclenchement soient toutes les minutes, c'est-à-dire le déclencheur # 0 = au temps 0, le déclencheur # 1 = 60000msec, # 2 = 120000, # 3 = 180000, etc. et le déclencheur # 0 au temps 0 déclenche mon travail, ce qui prend 130000msec. Avec un Job simple, cela exécuterait les déclencheurs # 1 et # 2 pendant que le trigger de travail # 0 est toujours en cours d'exécution. Avec un StatefulJob, cela exécuterait les déclencheurs # 1 et # 2 dans l'ordre, immédiatement après la fin de # 0 à 130000. Je ne veux pas, je veux que # 1 et # 2 ne s'exécutent pas et le prochain déclencheur qui exécute un travail devrait avoir lieu au # 3 (180000msec). Je dois donc encore faire autre chose avec StatefulJob pour le faire fonctionner comme je le souhaite, donc je ne vois pas grand avantage à l'utiliser.

  2. Utilisez un TriggerListener pour renvoyer true depuis vetoJobExecution ().

    Bien que la mise en œuvre de l'interface semble simple, je dois comprendre comment configurer une instance d'un TriggerListener de manière déclarative. Impossible de trouver les documents du fichier xml .

  3. Utilisez un static objet thread-safe partagé (par exemple un sémaphore ou autre) appartenant à ma classe qui implémente Job.

    Je n'aime pas l'idée d'utiliser des singletons via le mot clé static sous Tomcat/Quartz, je ne sais pas s'il y a des effets secondaires. De plus, je ne veux vraiment pas qu'ils soient de vrais singletons, juste quelque chose qui est associé à une définition de travail particulière.

  4. Implémentez mon propre Trigger qui étend SimpleTrigger et contient un état partagé qui pourrait exécuter son propre TriggerListener.

    Encore une fois, je ne sais pas comment configurer le fichier XML pour utiliser ce déclencheur plutôt que le standard <trigger><simple>...</simple></trigger>.

42
Jason S

lorsque votre travail Quartz se réveille, vous pouvez faire:

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
    }
18
dimitrisli

Il existe une autre solution plus simple. Le travail peut recevoir une annotation de DisallowConcurrentExecution qui empêche l'exécution simultanée de plusieurs instances. Voir les documents ici .

Le lien ne cesse de se rompre alors voici l'exemple pertinent.

@DisallowConcurrentExecution
public class ColorJob implements Job {
60
Ari Maniatis

la réponse de Dimitrisli n'est pas complète, alors voici la mienne.

Lorsque Quartz Job se réveille, il vous renvoie son JobExecutionContext. Je suppose que vous souhaitez ignorer les travaux avec le même déclencheur.

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

            }

Nous obtenons actuellement les contextes de travail et vérifions s'il existe une instance de travail précédente avec le même déclencheur. Si tel est le cas, nous sautons simplement avec retour.

23
Sezin Karli

J'ai accompli quelque chose de similaire en faisant implémenter mes classes de travaux StatefulJob , ce qui garantit qu'aucun autre travail ne démarre avant la fin du travail en cours d'exécution.

J'espère que cela pourra aider ;)

PD: Je l'ai implémenté en utilisant JBoss ... mais je ne pense pas que cela fasse une différence.

5
Ramses

pourriez-vous définir le travail en tant que StatefulJob, et pour chaque déclencheur que vous créez, définissez MisfireInstruction pour que le travail ne se déclenche pas s'il est manqué? Vous ne savez pas quel type de travail vous utilisez, mais vous devrez faire des recherches sur les instructions de mise à feu disponibles pour votre type de déclencheur.

Merci: D

4
DMan

Si vous utilisez 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);
    }
    ...
}
3
Paul Vargas

Légère variation à la solution 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 solution de scaramouche échoue lorsqu'une seule instance est utilisée pour toutes les JobExecutions (renvoyant le singleton à l'aide d'une classe JobFactory personnalisée, plutôt que d'appeler newInstance () pour chaque exécution)

1
Niranjan