Pengenalan kepada Kuarza

1. Gambaran keseluruhan

Quartz adalah kerangka penjadwalan kerja sumber terbuka yang ditulis sepenuhnya di Java dan dirancang untuk digunakan dalam aplikasi J2SE dan J2EE . Ia menawarkan fleksibiliti yang besar tanpa mengorbankan kesederhanaan.

Anda boleh membuat jadual yang rumit untuk melaksanakan sebarang pekerjaan. Contohnya ialah tugas yang dijalankan setiap hari, setiap hari Jumaat pada jam 7:30 malam atau hanya pada hari terakhir setiap bulan.

Dalam artikel ini, kita akan melihat elemen untuk membina pekerjaan dengan Quartz API. Untuk pengenalan dalam kombinasi dengan Spring, kami mengesyorkan Penjadualan pada Spring dengan Quartz.

2. Pergantungan Maven

Kita perlu menambahkan kebergantungan berikut ke pom.xml:

 org.quartz-scheduler quartz 2.3.0 

Versi terbaru boleh didapati di repositori Maven Central.

3. API Kuarza

Inti kerangka kerja adalah Penjadual . Ini bertanggungjawab untuk menguruskan persekitaran waktu proses untuk aplikasi kami.

Untuk memastikan skalabiliti, Quartz didasarkan pada seni bina pelbagai utas. Apabila dimulakan, kerangka kerja menginisialisasi sekumpulan utas pekerja yang digunakan oleh Penjadual untuk melaksanakan Pekerjaan .

Ini adalah bagaimana kerangka kerja dapat menjalankan banyak Pekerjaan secara serentak. Ia juga bergantung pada sekumpulan komponen pengurusan ThreadPool yang digabungkan secara longgar untuk menguruskan persekitaran utas .

Antara muka utama API adalah:

  • Penjadual - API utama untuk berinteraksi dengan penjadual kerangka
  • Pekerjaan - antara muka yang akan dilaksanakan oleh komponen yang ingin kita laksanakan
  • JobDetail - digunakan untuk menentukan kejadian menerusi s
  • Pencetus - komponen yang menentukan jadual di mana Pekerjaan tertentu akan dilakukan
  • JobBuilder - digunakan untuk membangun instance JobDetail , yang menentukan contoh Jobs
  • TriggerBuilder - digunakan untuk membina contoh Trigger

Mari kita perhatikan setiap komponen tersebut.

4. Penjadual

Sebelum kita dapat menggunakan Penjadual , perlu dibuat contoh. Untuk melakukan ini, kita boleh menggunakan kilang SchedulerFactory :

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

A Scheduler 's kitaran hayat disempadani oleh penciptaan, melalui SchedulerFactory dan panggilan kepada yang penutupan () kaedah. Setelah dibuat antara muka Penjadual dapat digunakan untuk menambah, menghapus, dan menyenaraikan Pekerjaan dan Pencetus , dan melakukan operasi lain yang berkaitan dengan penjadualan (seperti menjeda pemicu).

Walau bagaimanapun, yang Scheduler tidak akan bertindak ke atas sebarang pencetus sehingga ia telah bermula dengan () permulaan kaedah :

scheduler.start();

5. Pekerjaan

A Job adalah kelas yang melaksanakan menerusi antara muka. Ia hanya mempunyai satu kaedah mudah:

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

Apabila pencetus Pekerjaan diaktifkan, kaedah eksekusi () dipanggil oleh salah satu utas pekerja penjadual.

The JobExecutionContext objek yang diluluskan kepada kaedah ini menyediakan contoh kerja, dengan maklumat tentang persekitaran runtime, satu pemegang kepada Scheduler yang dilaksanakan itu, pemegang kepada Trigger yang mencetuskan pelaksanaan, tugas JobDetail objek, dan beberapa barangan lain .

The JobDetail objek dicipta oleh pelanggan kuarza yang pada masa menerusi ditambah kepada Penjadual. Ini pada dasarnya adalah definisi contoh pekerjaan :

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

Objek ini mungkin juga berisi berbagai pengaturan properti untuk Job , serta JobDataMap , yang dapat digunakan untuk menyimpan informasi keadaan untuk contoh tertentu dari kelas pekerjaan kita.

5.1. PetaData Kerja

The JobDataMap digunakan untuk memegang apa-apa jumlah data objek yang kita ingin menjadikan tersedia untuk contoh kerja apabila ia melaksanakan. JobDataMap adalah implementasi antara muka Peta Java dan memiliki beberapa metode kemudahan tambahan untuk menyimpan dan mengambil data jenis primitif.

Berikut adalah contoh memasukkan data ke dalam JobDataMap semasa membuat JobDetail , sebelum menambahkan tugas ke penjadual:

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

Dan berikut adalah contoh cara mengakses data ini semasa pelaksanaan tugas:

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

Contoh di atas akan mencetak "Job berkata Hello World !, dan nilai adalah 3.141".

Kami juga boleh menambahkan kaedah setter ke kelas pekerjaan kami yang sesuai dengan nama kunci dalam JobDataMap.

Sekiranya kita melakukan ini, pelaksanaan JobFactory lalai Quartz secara automatik memanggil setter tersebut ketika pekerjaan dilakukan, sehingga mencegah perlunya mengeluarkan nilai secara eksplisit dari peta dalam kaedah pelaksanaan kita.

6. Pencetus

Objek pencetus digunakan untuk memicu pelaksanaan Jobs .

Apabila kita ingin menjadwalkan Pekerjaan , kita perlu membuat pemicu dan menyesuaikan sifatnya untuk mengkonfigurasi keperluan penjadualan kita:

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

A Trigger may also have a JobDataMap associated with it. This is useful for passing parameters to a Job that are specific to the executions of the trigger.

There are different types of triggers for different scheduling needs. Each one has different TriggerKey properties for tracking their identities. However, some other properties are common to all trigger types:

  • The jobKey property indicates the identity of the job that should be executed when the trigger fires.
  • The startTime property indicates when the trigger’s schedule first comes into effect. The value is a java.util.Date object that defines a moment in time for a given calendar date. For some trigger types, the trigger fires at the given start time. For others, it simply marks the time that the schedule should start.
  • The endTime property indicates when the trigger’s schedule should be canceled.

Quartz ships with a handful of different trigger types, but the most commonly used ones are SimpleTrigger and CronTrigger.

6.1. Priority

Sometimes, when we have many triggers, Quartz may not have enough resources to immediately fire all of the jobs are scheduled to fire at the same time. In this case, we may want to control which of our triggers gets available first. This is exactly what the priority property on a trigger is used for.

For example, when ten triggers are set to fire at the same time and merely four worker threads are available, the first four triggers with the highest priority will be executed first. When we do not set a priority on a trigger, it uses a default priority of five. Any integer value is allowed as a priority, positive or negative.

In the example below, we have two triggers with a different priority. If there aren't enough resources to fire all the triggers at the same time, triggerA will be the first one to be fired:

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. Misfire Instructions

A misfire occurs if a persistent trigger misses its firing time because of the Scheduler being shut down, or in case there are no available threads in Quartz’s thread pool.

The different trigger types have different misfire instructions available. By default, they use a smart policy instruction. When the scheduler starts, it searches for any persistent triggers that have misfired. After that, it updates each of them based on their individually configured misfire instructions.

Let's take a look at the examples below:

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();

We have scheduled the trigger to run 10 seconds ago (so it is 10 seconds late by the time it is created) to simulate a misfire, e.g. because the scheduler was down or didn't have a sufficient amount of worker threads available. Of course, in a real-world scenario, we would never schedule triggers like this.

In the first trigger (misFiredTriggerA) no misfire handling instructions are set. Hence a called smart policy is used in that case and is called: withMisfireHandlingInstructionFireNow(). This means that the job is executed immediately after the scheduler discovers the misfire.

The second trigger explicitly defines what kind of behavior we expect when misfiring occurs. In this example, it just happens to be the same smart policy.

6.3. SimpleTrigger

SimpleTrigger is used for scenarios in which we need to execute a job at a specific moment in time. This can either be exactly once or repeatedly at specific intervals.

An example could be to fire a job execution at exactly 12:20:00 AM on January 13, 2018. Similarly, we can start at that time, and then five more times, every ten seconds.

In the code below, the date myStartTime has previously been defined and is used to build a trigger for one particular timestamp:

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

Next, let's build a trigger for a specific moment in time, then repeating every ten seconds ten times:

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

6.4. CronTrigger

The CronTrigger is used when we need schedules based on calendar-like statements. For example, we can specify firing-schedules such as every Friday at noon or every weekday at 9:30 am.

Cron-Expressions are used to configure instances of CronTrigger. These expressions consist of Strings that are made up of seven sub-expressions. We can read more about Cron-Expressions here.

In the example below, we build a trigger that fires every other minute between 8 am and 5 pm, every day:

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

7. Conclusion

Dalam artikel ini, kami telah menunjukkan cara membuat Penjadual untuk memicu Pekerjaan . Kami juga melihat beberapa pilihan pencetus yang paling biasa digunakan: SimpleTrigger dan CronTrigger .

Kuarza boleh digunakan untuk membuat jadual sederhana atau kompleks untuk melaksanakan puluhan, ratusan, atau lebih banyak pekerjaan. Maklumat lebih lanjut mengenai rangka kerja boleh didapati di laman web utama.

Kod sumber contoh boleh didapati di GitHub.