45 votes

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

Cela devrait être très facile. J'utilise Quartz qui tourne sous Apache Tomcat 6.0.18, et j'ai une fichier jobs.xml qui met en place mon travail planifié qui s'exécute toutes les minutes.

Ce que j'aimerais faire, c'est que si la tâche est toujours en cours au moment du prochain déclenchement, je ne veux pas lancer une nouvelle tâche, afin de laisser l'ancienne instance se terminer.

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

Si ce n'est pas le cas, existe-t-il un moyen de partager l'accès à un singleton en mémoire au sein de l'interface utilisateur de mon application ? Emploi (est-ce par le biais de la JobExecutionContext ?) afin que je puisse gérer moi-même la concurrence ? (et détecter si une instance précédente est en cours d'exécution)


mettre à jour : Après avoir cherché dans la documentation, voici quelques approches que j'envisage, mais soit je ne sais pas comment les faire fonctionner, soit il y a des problèmes.

  1. Utilisez StatefulJob . Cela empêche l'accès simultané... mais je ne suis pas sûr des autres effets secondaires qui se produiraient si je l'utilisais, je veux aussi éviter la situation suivante :

    Supposons que les temps de déclenchement soient toutes les minutes, c'est-à-dire que le trigger#0 = au temps 0, le trigger #1 = 60000msec, #2 = 120000, #3 = 180000, etc. et le trigger#0 au temps 0 déclenche mon job qui prend 130000msec. Avec un job ordinaire, les déclencheurs #1 et #2 sont exécutés pendant que le déclencheur #0 est toujours en cours. Avec un StatefulJob, les triggers #1 et #2 seraient exécutés dans l'ordre, immédiatement après que le #0 se soit terminé à 130000. Ce n'est pas ce que je veux, je veux que les triggers #1 et #2 ne s'exécutent pas et que le prochain trigger qui lance un job ait lieu à #3 (180000msec). Je dois donc encore faire quelque chose d'autre avec StatefulJob pour qu'il fonctionne comme je le souhaite, et je ne vois pas vraiment d'avantage à l'utiliser.

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

    Bien que l'implémentation de l'interface semble simple, je dois trouver comment configurer une instance d'un TriggerListener de manière déclarative. Je ne trouve pas les documents relatifs au fichier xml. .

  3. Utilisez un static objet partagé à sécurité thread (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 l'option static sous Tomcat/Quartz, je ne suis pas sûr qu'il y ait des effets secondaires. De plus, je ne veux pas vraiment qu'ils soient de véritables singletons, juste quelque chose qui est associé à une définition de travail particulière.

  4. Mettre en œuvre mes propres Déclencheur qui prolonge 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> .

8 votes

Dans les nouvelles versions de Quartz, le marquage de la classe implémentant le travail avec @DisallowConcurrentExecution permet de s'assurer qu'une seule classe s'exécute à la fois...

66voto

Ari Maniatis Points 1076

Il existe une autre solution plus simple. On peut attribuer à la tâche une annotation DisallowConcurrentExecution qui empêche l'exécution de plusieurs instances simultanées. Voir la documentation aquí .

Le lien ne cesse de se rompre, alors voici l'échantillon pertinent.

@DisallowConcurrentExecution
public class ColorJob implements Job {

12 votes

L'utilisation de l'annotation DisallowConcurrentExecution met l'instance actuelle en attente d'exécution jusqu'à la fin de l'instance précédente. Existe-t-il un moyen d'empêcher l'exécution de l'instance actuelle si une instance de travail est déjà en cours d'exécution ?

2 votes

Sachez toutefois que cette restriction ne s'applique qu'aux emplois ayant la même JobKey (ce qui est généralement le cas). Les tâches avec des JobKeys différentes peuvent toujours s'exécuter simultanément, même si elles sont implémentées avec la même classe.

25voto

Sezin Karli Points 634

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

Lorsque le job Quartz se réveille, il vous renvoie son JobExecutionContext. Je suppose que vous voulez sauter des jobs avec le même trigger.

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 récupérons les contextes de travail actuels et vérifions s'il existe une instance de travail précédente avec le même déclencheur. Si c'est le cas, nous passons directement au retour.

18voto

dimitrisli Points 5203

Quand votre job Quartz se réveille, vous pouvez le 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
}

1 votes

Cela semble intéressant... vous devez vérifier le JobExecutionContext existant, sinon vous obtiendrez toujours l'exception. Il y a aussi une exception lancée par sched.getCurrentlyExecutingJobs() qui doit être attrapée.

5voto

Ramses Points 110

J'ai accompli quelque chose de similaire en mettant en place mes classes de travail StatefulJob ce qui garantit qu'aucun autre travail ne démarre avant la fin du travail en cours.

J'espère que cela vous aidera ;)

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

0 votes

Aide un peu mais pas vraiment, voir mon commentaire n°1 ci-dessus.

4voto

DMan Points 41

Pourriez-vous définir le travail comme un StatefulJob, et pour chaque déclencheur que vous créez, définir la MisfireInstruction pour que le travail ne se déclenche pas s'il est manqué ? Je ne suis pas sûr du type de job que vous utilisez mais vous devrez faire quelques recherches sur les MisfireInstructions disponibles pour votre type de trigger.

Merci, D

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X