web-dev-qa-db-fra.com

Assurez-vous que l'exécution du travail Spring Quartz ne se chevauche pas

J'ai un programme Java qui s'exécute à partir de Spring Qquartz toutes les 20 secondes. Parfois, l'exécution ne prend que quelques secondes, mais à mesure que les données grossissent, je suis sûr qu'elles s'exécutent pendant 20 secondes ou plus.

Comment puis-je empêcher Quartz de déclencher/déclencher le travail alors qu'une instance est toujours en cours d'exécution? Déclencher 2 travaux effectuant les mêmes opérations sur une base de données ne serait pas si bon. Existe-t-il un moyen de faire une sorte de synchronisation?

45
ant

Si tout ce que vous avez à faire est de tirer toutes les 20 secondes, le Quartz est une surpuissance grave. Le Java.util.concurrent.ScheduledExecutorService devrait être parfaitement suffisant pour ce travail.

ScheduledExecutorService fournit également deux sémantiques pour la planification. "taux fixe" tentera d'exécuter votre travail toutes les 20 secondes quel que soit le chevauchement, tandis que "délai fixe" tentera de laisser 20 secondes entre la fin du premier travail et le début de la suivante. Si vous souhaitez éviter les chevauchements, le délai fixe est le plus sûr.

31
skaffman

Quartz 1

Si vous changez votre classe pour implémenter StatefulJob au lieu de Job, Quartz s'en chargera pour vous. Depuis le StatefulJob javadoc :

les travaux avec état ne sont pas autorisés à s'exécuter simultanément, ce qui signifie que les nouveaux déclencheurs qui se produisent avant la fin de la méthode d'exécution (xx) seront retardés.

StatefulJob étend Job et n'ajoute aucune nouvelle méthode, donc tout ce que vous devez faire pour obtenir le comportement que vous souhaitez est de changer ceci:

public class YourJob implements org.quartz.Job {
    void execute(JobExecutionContext context) {/*implementation omitted*/}
}

Pour ça:

public class YourJob implements org.quartz.StatefulJob {
    void execute(JobExecutionContext context) {/*implementation omitted*/}
}

Quartz 2

Dans la version 2.0 de Quartz, StatefulJob est déconseillé. Il est désormais recommandé d'utiliser des annotations à la place, par ex.

@DisallowConcurrentExecution
public class YourJob implements org.quartz.Job {
    void execute(JobExecutionContext context) {/*implementation omitted*/}
}
133
Dónal

Juste au cas où quelqu'un ferait référence à cette question, StatefulJob a été déprécié. Ils vous suggèrent désormais d'utiliser des annotations à la place ...

@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class TestJob implements Job {

Cela expliquera ce que ces annotations signifient ...

Les annotations provoquent un comportement tel que leurs noms le décrivent - plusieurs instances du travail ne seront pas autorisées à s'exécuter simultanément (considérons un cas où un travail a du code dans sa méthode execute () qui prend 34 secondes pour s'exécuter, mais il est planifié avec un déclencheur qui se répète toutes les 30 secondes) et dont le contenu JobDataMap sera conservé dans le JobStore du planificateur après chaque exécution. Pour les besoins de cet exemple, seule l'annotation @PersistJobDataAfterExecution est vraiment pertinente, mais il est toujours judicieux d'utiliser l'annotation @DisallowConcurrentExecution avec elle, pour éviter les conditions de concurrence sur les données enregistrées.

19
gshauger

si vous utilisez du quartz de printemps, je pense que vous devez configurer comme ça

    <bean id="batchConsumerJob"class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="myScheduler" />
        <property name="targetMethod" value="execute" />
        <property name="concurrent" value="false" />
    </bean>
4
Paul

Je ne suis pas sûr que vous souhaitiez la synchronisation, car la deuxième tâche se bloquera jusqu'à la fin de la première, et vous vous retrouverez avec un retard. Vous pouvez placer les travaux dans une file d'attente, mais d'après votre description, il semble que la file d'attente puisse augmenter indéfiniment.

Je voudrais enquêter ReadWriteLock s, et laisser votre tâche définir un verrou pendant son exécution. Les tâches futures peuvent inspecter ce verrou et quitter immédiatement si une ancienne tâche est toujours en cours d'exécution. J'ai découvert par expérience que c'est la manière la plus fiable d'aborder cela.

Peut-être générer également un avertissement afin que vous sachiez que vous rencontrez des problèmes et augmenter l'intervalle de temps en conséquence?

2
Brian Agnew

Vous pouvez utiliser un sémaphore. Lorsque le sémaphore est pris, abandonnez le 2e travail et attendez le prochain feu.

0
Salandur

les mettre dans une file d'attente

Même si le temps dépasse 20 secondes, le travail en cours doit être terminé et le suivant doit être récupéré dans la file d'attente.

Ou vous pouvez également augmenter le temps jusqu'à un montant raisonnable.

0
Asad Khan