2010-04-20 8 views
39

Dies sollte wirklich einfach sein. Ich benutze Quartz läuft unter Apache Tomcat 6.0.18, und ich habe eine jobs.xml file, die meinen geplanten Job eingerichtet, die jede Minute ausgeführt wird.quartz: gleichzeitige Instanzen eines Jobs in jobs.xml verhindern

Was ich tun möchte, ist, wenn der Job noch läuft, wenn die nächste Triggerzeit rollt, will ich keinen neuen Job starten, damit ich die alte Instanz abschließen kann.

Gibt es eine Möglichkeit, dies in jobs.xml anzugeben (gleichzeitige Instanzen zu verhindern)?

Wenn nicht, gibt es eine Möglichkeit, den Zugriff auf ein In-Memory-Singleton innerhalb der Job Implementierung meiner Anwendung zu teilen (ist dies durch die JobExecutionContext?), So dass ich die Nebenläufigkeit selbst behandeln kann? (Und erkennen, ob eine vorherige Instanz läuft)


Update: Nach zappelte um in der Dokumentation, hier ein paar Ansätze, die ich bin am überlegen, aber entweder nicht wissen, wie sie an die Arbeit, oder es gibt Probleme.

  1. Verwenden Sie StatefulJob. Dies verhindert den gleichzeitigen Zugriff ... aber ich bin nicht sicher, welche anderen Nebenwirkungen auftreten würden, wenn ich es benutze, auch möchte ich die folgende Situation vermeiden:

    Angenommen, die Triggerzeiten würden jede Minute sein, dh Trigger # 0 = zum Zeitpunkt 0, Auslöser # 1 = 60000 ms, # 2 = 120000, # 3 = 180000 usw. und der Auslöser # 0 zum Zeitpunkt 0 löst meinen Job aus, der 130000 ms benötigt. Bei einem einfachen Job würde dies die Trigger # 1 und # 2 ausführen, während der Job-Trigger # 0 noch läuft. Mit einem StatefulJob würde dies die Trigger # 1 und # 2 in der Reihenfolge ausführen, unmittelbar nachdem # 0 bei 130000 endet. Ich möchte das nicht, ich möchte # 1 und # 2 nicht ausführen und der nächste Trigger, der einen Job ausführt, sollte stattfinden bei # 3 (180000msec). Ich muss also noch etwas anderes mit StatefulJob tun, damit es so funktioniert, wie ich es möchte. Daher sehe ich keinen großen Vorteil darin, es zu benutzen.

  2. Verwenden Sie eine TriggerListener, um von vetoJobExecution() den Wert true zurückzugeben.

    Obwohl die Implementierung der Schnittstelle einfach scheint, muss ich herausfinden, wie man eine Instanz eines TriggerListener deklarativ aufbaut. Can't find the docs for the xml file.

  3. Verwenden Sie ein static freigegebenes Thread-sicheres Objekt (z. B. ein Semaphor oder was auch immer), das zu meiner Klasse gehört, die Job implementiert.

    Ich mag nicht die Idee der Verwendung von Singletons über das Schlüsselwort static unter Tomcat/Quarz, nicht sicher, ob es Nebenwirkungen gibt. Ich will auch nicht, dass sie echte Singletons sind, nur etwas, das mit einer bestimmten Jobdefinition in Verbindung gebracht wird.

  4. Implementieren Sie meine eigenen Trigger, die erweitert und Shared-Status enthält, die einen eigenen TriggerListener ausführen könnte.

    Noch einmal, ich weiß nicht, wie Sie die XML-Datei für die Verwendung dieses Triggers anstelle des Standards <trigger><simple>...</simple></trigger> einrichten.

+8

In neueren Versionen von Quarz, Kennzeichnung der Klasse Umsetzung Job mit @DisallowConcurrentExecution stellt sicher, dass nur eine Klasse jederzeit läuft ... – sbrattla

Antwort

17

wenn Ihr Quartz Job aufwacht können Sie tun:

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

Sieht interessant aus ... Sie müssen gegen den vorhandenen JobExecutionContext überprüfen, sonst erhalten Sie immer die Ausnahme. Es gibt auch eine Ausnahme von sched.getCurrentlyExecutingJobs(), die abgefangen werden muss. –

5

ich erreichte etwas ähnliches meiner Job-Klassen StatefulJob implementieren zu machen, die dafür sorgt, dass keine andere Aufträge beginnen, bevor der laufende Job beendet.

Hoffnung, die hilft;)

PD: Ich setzte sie JBoss mit ... aber ich glaube nicht, dass ein Unterschied macht.

+0

hilft ein wenig, aber nicht wirklich, 1 über meinem Kommentar # sehen. –

+1

@youghobbit, Siehe aktualisierte Antwort – Ramses

4

Sie den Job als StatefulJob einstellen könnte, und für jeden Trigger Sie die MisfireInstruction für den Job eingestellt schaffen, um nicht ausgelöst, wenn es verpasst? Sie sind sich nicht sicher, welche Art von Job Sie verwenden, aber Sie müssen etwas über die Fehlzündungsanweisungen herausfinden, die für Ihren Triggertyp verfügbar sind.

Danke, D

20

Antwort des dimitrisli nicht so hier vollständig ist meins.

Wenn Quartz Job aufwacht, gibt es seinen JobExecutionContext zurück. Ich nehme an, Sie möchten Jobs mit dem gleichen Auslöser überspringen.

Wir erhalten derzeit Jobkontexte und prüfen, ob es eine vorherige Jobinstanz mit dem gleichen Trigger gibt. Wenn dies der Fall ist, überspringen wir einfach mit der Rückkehr.

56

Es gibt eine andere einfachere Lösung. Der Job kann eine Annotation zu DisallowConcurrentExecution erhalten, die verhindert, dass mehrere gleichzeitige Instanzen ausgeführt werden. Siehe Dokumentation here.

Die Verbindung bricht, also hier ist das relevante Beispiel.

+7

Mithilfe der Annotation "DisallowConcurrentExecution" wird die aktuelle Instanz für die Ausführung in die Warteschlange gestellt, bis die vorherige Instanz beendet wird. Gibt es eine Möglichkeit, die aktuelle Instanz aus der Ausführung zu löschen, wenn eine Job-Instanz bereits ausgeführt wird? –

+2

Beachten Sie jedoch, dass die Einschränkung nur für Jobs mit demselben JobKey gilt (was normalerweise der Fall ist). Jobs mit verschiedenen JobKeys können immer noch gleichzeitig ausgeführt werden, auch wenn sie mit derselben Klasse implementiert sind. – stenix

1

Geringfügige Abweichung von der Scaramouche-Lösung.

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

} 

scaramouche Lösung schlägt fehl, wenn eine einzelne Instanz dort für alle JobExecutions (Rückgabe des Singleton mit einer benutzerdefinierten JobFactory Klasse, anstatt Aufruf newInstance() für jede Ausführung)

1

Wenn Sie org.springframework.scheduling.quartz.QuartzJobBean verwenden verwendet wird:

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