Einführung in Quarz

1. Überblick

  • http://www.quartz-scheduler.org/ ist ein vollständig in Java geschriebenes Open Source-Job-Scheduling-Framework, das für die Verwendung in J2SE - und J2EE -Anwendungen konzipiert ist. Es bietet große Flexibilität ohne Einbußen bei der Einfachheit. **

Sie können komplexe Zeitpläne für die Ausführung eines Jobs erstellen. Beispiele sind z. Aufgaben, die täglich ausgeführt werden, jeden zweiten Freitag um 19:30 Uhr. oder nur am letzten Tag eines jeden Monats.

In diesem Artikel werden Elemente beschrieben, um einen Job mit der Quartz-API zu erstellen. Für eine Einführung in Kombination mit Spring empfehlen wir Scheduling in spring with quarz .

2. Abhängigkeiten von Maven

Wir müssen der pom.xml die folgende Abhängigkeit hinzufügen:

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.0</version>
</dependency>

Die neueste Version finden Sie unter Maven Zentrales Repository .

3. Die Quarz-API

Das Herzstück des Frameworks ist der Scheduler . Es ist für die Verwaltung der Laufzeitumgebung für unsere Anwendung verantwortlich.

Um die Skalierbarkeit sicherzustellen, basiert Quartz auf einer Multithreading-Architektur.

  • Beim Start initialisiert das Framework eine Reihe von Arbeitsthreads ** , die vom Scheduler zur Ausführung von Jobs verwendet werden.

Auf diese Weise kann das Framework viele Jobs gleichzeitig ausführen. Für die Verwaltung der Thread-Umgebung ist außerdem eine lose verknüpfte Gruppe von ThreadPool -Verwaltungskomponenten erforderlich.

Die wichtigsten Schnittstellen der API sind:

  • Scheduler – die primäre API für die Interaktion mit dem Scheduler von

der Rahmen ** Job – eine Schnittstelle, die von Komponenten implementiert werden soll, die wir möchten

ausgeführt haben ** JobDetail – wird verwendet, um Instanzen von __Job __s zu definieren

  • Trigger – eine Komponente, die den Zeitplan bestimmt, nach dem a

gegeben Job wird ausgeführt ** JobBuilder – zur Erstellung von JobDetail -Instanzen, die definieren

Instanzen von Jobs ** TriggerBuilder – zur Erstellung von Trigger -Instanzen

Sehen wir uns jede dieser Komponenten an.

4. Planer

Bevor wir den Scheduler verwenden können, muss er instanziiert werden. Dazu können wir die Factory SchedulerFactory verwenden:

SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();

Der Lebenszyklus eines Scheduler ist durch seine SchedulerFactory und einen Aufruf seiner shutdown () -Methode von seiner Erstellung begrenzt. Nach der Erstellung der Scheduler -Schnittstelle können Jobs und Triggers hinzugefügt, entfernt und aufgelistet sowie andere terminierungsbezogene Vorgänge ausgeführt werden (z. B. das Anhalten eines Triggers).

Der Scheduler wirkt jedoch nicht auf Auslöser, bis er mit der start () -Methode gestartet wurde:

scheduler.start();

5. Arbeitsplätze

Ein Job ist eine Klasse, die das Job -Interface implementiert. Es gibt nur eine einfache Methode:

public class SimpleJob implements Job {
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        System.out.println("This is a quartz job!");
    }
}

Wenn der Job’s -Trigger ausgelöst wird, wird die execute () -Methode von einem der Worker-Threads des Schedulers aufgerufen.

Das JobExecutionContext -Objekt, das an diese Methode übergeben wird, enthält die Jobinstanz mit Informationen zur Laufzeitumgebung, ein Handle für den Scheduler , das sie ausgeführt hat, ein Handle für den Trigger , der die Ausführung ausgelöst hat, das JobDetail -Objekt des Jobs und einige andere Elemente .

Das JobDetail -Objekt wird vom Quartz-Client zu dem Zeitpunkt erstellt, zu dem der Job zum __Scheduler hinzugefügt wird. Es handelt sich im Wesentlichen um die Definition der Jobinstanz:

JobDetail job = JobBuilder.newJob(SimpleJob.class)
  .withIdentity("myJob", "group1")
  .build();

Dieses Objekt kann auch verschiedene Eigenschaftseinstellungen für den Job sowie eine JobDataMap enthalten, mit der Statusinformationen für eine bestimmte Instanz unserer Jobklasse gespeichert werden können.

5.1. JobDataMap

Die JobDataMap wird verwendet, um eine beliebige Anzahl von Datenobjekten aufzunehmen, die wir der Jobinstanz bei ihrer Ausführung zur Verfügung stellen möchten. JobDataMap ist eine Implementierung der Java-Schnittstelle Map und verfügt über einige bequeme Methoden zum Speichern und Abrufen von Daten primitiver Typen.

Hier ein Beispiel, wie Sie beim Erstellen von JobDetail Daten in die JobDataMap einfügen, bevor Sie den Job zum Scheduler hinzufügen:

JobDetail job = newJob(SimpleJob.class)
  .withIdentity("myJob", "group1")
  .usingJobData("jobSays", "Hello World!")
  .usingJobData("myFloatValue", 3.141f)
  .build();

Hier ein Beispiel, wie Sie während der Ausführung des Jobs auf diese Daten zugreifen können:

public class SimpleJob implements Job {
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();

        String jobSays = dataMap.getString("jobSays");
        float myFloatValue = dataMap.getFloat("myFloatValue");

        System.out.println("Job says: " + jobSays + ", and val is: " + myFloatValue);
    }
}

Das obige Beispiel druckt "Job sagt Hallo Welt! Und val ist 3.141".

Wir können unserer Jobklasse auch Setter-Methoden hinzufügen, die den Namen der Schlüssel in der JobDataMap. entsprechen.

In diesem Fall ruft die standardmäßige JobFactory -Implementierung von Quartz diese Setter automatisch auf, wenn der Job instanziiert wird. Dadurch ist es nicht erforderlich, die Werte innerhalb der Ausführungsmethode explizit aus der Map zu holen.

6. Löst aus

Trigger -Objekte werden verwendet, um die Ausführung von Jobs auszulösen.

Wenn wir einen Job planen möchten, müssen wir einen Trigger instanziieren und seine Eigenschaften anpassen, um unsere Zeitplanungsanforderungen zu konfigurieren:

Trigger trigger = TriggerBuilder.newTrigger()
  .withIdentity("myTrigger", "group1")
  .startNow()
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withIntervalInSeconds(40)
    .repeatForever())
  .build();

Einem Trigger kann auch eine JobDataMap zugeordnet sein. Dies ist nützlich, um Parameter an einen Job zu übergeben, die für die Ausführung des Triggers spezifisch sind.

Es gibt verschiedene Arten von Triggern für unterschiedliche Terminierungsanforderungen.

Jeder hat unterschiedliche TriggerKey -Eigenschaften zum Nachverfolgen seiner Identitäten. Einige andere Eigenschaften sind jedoch allen Auslösertypen gemeinsam:

  • Die jobKey -Eigenschaft gibt die Identität des Jobs an, der sein sollte

Wird ausgeführt, wenn der Abzug ausgelöst wird.

  • Die Eigenschaft startTime gibt an, wann der Zeitplan des Auslösers zuerst

tritt in Kraft Der Wert ist ein java.util.Date -Objekt, das einen Zeitpunkt für ein bestimmtes Kalenderdatum definiert. Bei einigen Auslösertypen wird der Auslöser zum angegebenen Startzeitpunkt ausgelöst. Für andere ist es einfach der Zeitpunkt, zu dem der Zeitplan beginnen soll.

  • Die endTime -Eigenschaft gibt an, wann der Zeitplan des Auslösers sein sollte

abgebrochen.

Quarz ist mit einer Handvoll verschiedener Trigger-Typen ausgestattet, die am häufigsten verwendeten sind jedoch SimpleTrigger und CronTrigger ** .

6.1. Priorität

Manchmal, wenn wir viele Auslöser haben, verfügt Quartz möglicherweise nicht über ausreichende Ressourcen, um sofort alle Aufträge abzufeuern, deren Ausführung gleichzeitig geplant ist. In diesem Fall möchten wir vielleicht steuern, welcher unserer Auslöser zuerst verfügbar ist. Genau dafür wird die priority -Eigenschaft eines Triggers verwendet.

  • Zum Beispiel ** : Wenn zehn Trigger gleichzeitig ausgelöst werden und nur vier Worker-Threads verfügbar sind, werden zuerst die ersten vier Trigger mit der höchsten Priorität ausgeführt. Wenn wir für einen Trigger keine Priorität festlegen, wird eine Standardpriorität von fünf verwendet. Jeder ganzzahlige Wert ist als Priorität zulässig, positiv oder negativ.

Im folgenden Beispiel haben wir zwei Auslöser mit unterschiedlicher Priorität. Wenn nicht genügend Ressourcen vorhanden sind, um alle Auslöser gleichzeitig auszulösen, wird triggerA der erste Auslöser:

Trigger triggerA = TriggerBuilder.newTrigger()
  .withIdentity("triggerA", "group1")
  .startNow()
  .withPriority(15)
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withIntervalInSeconds(40)
    .repeatForever())
  .build();

Trigger triggerB = TriggerBuilder.newTrigger()
  .withIdentity("triggerB", "group1")
  .startNow()
  .withPriority(10)
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withIntervalInSeconds(20)
    .repeatForever())
  .build();

6.2. Aussetzeranweisungen

  • Ein Fehlzündungsfehler tritt auf, wenn ein permanenter Auslöser seine Auslösezeit aufgrund des Herunterfahrens des Scheduler auslöst oder wenn im Thread-Pool von Quartz keine verfügbaren Threads vorhanden sind. **

Für die verschiedenen Auslösertypen stehen verschiedene Aussetzanweisungen zur Verfügung. Standardmäßig verwenden sie eine intelligente Richtlinienanweisung. Wenn der Scheduler gestartet wird, sucht er nach dauerhaften Triggern, die einen Fehlzündungsfehler hatten. Danach werden alle auf der Grundlage ihrer individuell konfigurierten Aussetzeranweisungen aktualisiert.

Schauen wir uns die Beispiele unten an:

Trigger misFiredTriggerA = TriggerBuilder.newTrigger()
  .startAt(DateUtils.addSeconds(new Date(), -10))
  .build();

Trigger misFiredTriggerB = TriggerBuilder.newTrigger()
  .startAt(DateUtils.addSeconds(new Date(), -10))
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withMisfireHandlingInstructionFireNow())
  .build();

Wir haben geplant, dass der Auslöser vor 10 Sekunden ausgeführt wird (es ist also 10 Sekunden später, als er erstellt wurde), um eine Fehlzündung zu simulieren, z. weil der Scheduler inaktiv war oder nicht genügend Worker-Threads zur Verfügung stand. Natürlich würden wir in einem realen Szenario niemals solche Auslöser einplanen.

Im ersten Auslöser ( misFiredTriggerA ) werden keine Anweisungen zur Behandlung von Fehlzündungen gesetzt. Daher wird in diesem Fall eine smart policy mit dem Namen __withMisfireHandlingInstructionFireNow () verwendet. Dies bedeutet, dass der Job sofort ausgeführt wird, nachdem der Scheduler die Aussetzer erkannt hat.

Der zweite Auslöser definiert explizit, welche Art von Verhalten wir erwarten, wenn Fehlzündungen auftreten. In diesem Beispiel handelt es sich einfach um dieselbe intelligente Richtlinie.

6.3. SimpleTrigger

  • SimpleTrigger wird für Szenarien verwendet, in denen ein Job zu einem bestimmten Zeitpunkt ausgeführt werden muss. ** Dies kann entweder genau einmal oder in bestimmten Abständen wiederholt werden.

Ein Beispiel könnte sein, eine Auftragsausführung am 13. Januar 2018 um exakt 12:20:00 Uhr abzufeuern. In ähnlicher Weise können wir zu diesem Zeitpunkt beginnen und dann alle zehn Sekunden fünfmal.

Im folgenden Code wurde das Datum myStartTime zuvor definiert und wird verwendet, um einen Trigger für einen bestimmten Zeitstempel zu erstellen :

SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
  .withIdentity("trigger1", "group1")
  .startAt(myStartTime)
  .forJob("job1", "group1")
  .build();

Als Nächstes erstellen wir einen Auslöser für einen bestimmten Zeitpunkt und wiederholen uns alle zehn Sekunden zehnmal:

SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
  .withIdentity("trigger2", "group1")
  .startAt(myStartTime)
  .withSchedule(simpleSchedule()
    .withIntervalInSeconds(10)
    .withRepeatCount(10))
  .forJob("job1")
  .build();

6.4. CronTrigger

  • Der CronTrigger wird verwendet, wenn Zeitpläne auf der Basis von kalenderartigen Anweisungen benötigt werden. ** Zum Beispiel können Feuerungszeitpläne angegeben werden, z.

Cron-Ausdrücke werden zum Konfigurieren von CronTrigger -Instanzen verwendet. Diese Ausdrücke bestehen aus Strings , die aus sieben Unterausdrücken bestehen. Wir können mehr über Cron-Expressions lesen: https://docs.oracle.com/cd/E12058 01/doc/doc.1014/e12030/cron expressions.htm

Im folgenden Beispiel erstellen wir einen Abzug, der jeden Tag zwischen 8.00 und 17.00 Uhr jeden zweiten Tag ausgelöst wird:

CronTrigger trigger = TriggerBuilder.newTrigger()
  .withIdentity("trigger3", "group1")
  .withSchedule(CronScheduleBuilder.cronSchedule("0 0/2 8-17 **  **  ?"))
  .forJob("myJob", "group1")
  .build();

7. Fazit

In diesem Artikel haben wir gezeigt, wie Sie einen Scheduler erstellen, um einen Job auszulösen. Wir haben auch einige der am häufigsten verwendeten Triggeroptionen gesehen:

SimpleTrigger und CronTrigger .

Mit Quartz können Sie einfache oder komplexe Zeitpläne erstellen, um Dutzende, Hunderte oder sogar mehr Aufträge auszuführen. Weitere Informationen zum Framework finden Sie auf der Hauptseite website .