Quartz調度框架

Quartz是什么

Quartz是一個開源的作業調度包,能夠運行在幾乎任何java項目中,小到單機應用,大到電商系統。Quartz能夠創建很容易的調度,也可以創建十個、百個、千個、甚至萬個任務的復雜調度。Quartz將任務定義成java組件,能夠執行幾乎任何你定義的事情。Quartz也支持許多企業級特性,比如JTA和集群。
Quartz的核心是Scheduler、Job、Trigger。Job負責定義需要執行的任務,Trigger負責調度策略,Scheduler負責將二者組合。

Quartz官網:http://www.quartz-scheduler.org/
quartz核心包quartz-2.2.1.jar
<pre>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
</pre>
如果想使用quartz內置的job還需要導入quartz-jobs
<pre>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
</pre>

簡單Demo

調度的任務,需要實現Job接口,實現execute()方法。execute方法就是需要執行的任務。
<pre>
public class MyJob implements Job {

public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
    System.out.println("Hello World,Hello Quartz!");
}

}
</pre>

調度流程
1、通過工廠方法創建調度器(quartz scheduler生命周期的開始)
2、啟動調度器
3、定義作業
4、創建觸發器
5、組合任務和觸發器
6、調度結束(quartz scheduler生命周期結束)
<pre>
public class HelloQuartz {
public static void main(String[] args){
try {
//通過工廠方法創建調度器,整個程序不會停止,直到調用scheduler.shutdown()
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//調度啟動
scheduler.start();
//定義作業,并且綁定我們指定的作業類
JobDetail job = newJob(MyJob.class).withIdentity("myJob","group1").build();
//創建觸發器
Trigger trigger = newTrigger().withIdentity("myTrigger","group1").startNow().withSchedule(simpleSchedule().withIntervalInSeconds(20).repeatForever()).build();
//調度器添加觸發器和作業,開始調度
scheduler.scheduleJob(job,trigger);
//調度關閉
// scheduler.shutdown();
}catch (SchedulerException e){
e.printStackTrace();
}
}
}
</pre>
Quartz的API設計使用的是 DSL(domain specific language),所以非常干凈。
如果你直接運行上面的代碼應該會拋出:
<pre>
org.quartz.SchedulerConfigException: Thread count must be > 0
at org.quartz.simpl.SimpleThreadPool.initialize(SimpleThreadPool.java:242)
at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:1288)
at org.quartz.impl.StdSchedulerFactory.getScheduler(StdSchedulerFactory.java:1519)
at org.quartz.impl.StdSchedulerFactory.getDefaultScheduler(StdSchedulerFactory.java:1535)
at com.yjz.quartz.HelloQuartz.main(HelloQuartz.java:21)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
</pre>
提示線程池中初始化的線程要大于0,那么這個線程池數量在哪里配置?原來Quartz使用一個quartz.properties的配置文件,你可以在你項目中classpath創建該文件(web項目在resources下)。quartz的配置文件還是比較好配置的,可以現簡單的配置幾項:
<pre>
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 3
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
</pre>

quartz核心接口

Scheduler:與調度器交互的主要API
Job:任務組件實現的接口
JobDetail:用來定義任務的實例
JobBuilder:用于定義JobDetail實例,定義作業實例
Trigger:為任務定義執行計劃的組件
TriggerBuilder:用來定義Trigger實例
Scheduler只有調用了start()方法之后才能觸發任務執行,quartz的整個生命周期從通過SchedulerFactory創建Scheduler實例到調用shutdown()方法。

Jobs

之前說過每個job要實現Job接口,job接口里面中定義了指定:
<pre>
public interface Job {
void execute(JobExecutionContext context)
throws JobExecutionException;
}
</pre>
當任務觸發后,調度器線程會調用excute()方法,JobExecutionContext提供了job運行時的信息,Scheduler、Trigger、JobDetail等都執行它。每次調度器執行job,都會新建job實例,每次job執行完成后,這個實例會被垃圾回收。還有就是這個job必須有一個無參的構造函數(調度器內部要使用)。
JobDetail:客戶端(我們程序)創建JobDetail,并且添加到調度器中,JobDetail包含了job的設置信息。
JobDataMap是JobDetail的一部分,它可以包含任何數量的數據對象,可以用來顯示執行過程。JobDataMap實現java Map接口,提供了一些原始的存儲和獲取。
向JobDataMap中添加數據:
<pre>
JobDetail job = newJob(MyJob.class).withIdentity("myJob","group1").usingJobData("key1","value1").usingJobData("key2",2).build();
</pre>
獲取JobMapData:
<pre>
public class MyJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey jobKey = context.getJobDetail().getKey();
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String k1 = dataMap.getString("key1");
int k2 = dataMap.getInt("key2");
System.out.println("Hello World,Hello Quartz!");
}
}
</pre>
JobMapData可以在job中添加一些值,比如:
<pre>
public class DumbJob implements Job {
public DumbJob() {
}
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey key = context.getJobDetail().getKey();
JobDataMap dataMap = context.getMergedJobDataMap(); // Note the difference from the previous example
String jobSays = dataMap.getString("jobSays");
float myFloatValue = dataMap.getFloat("myFloatValue");
ArrayList state = (ArrayList)dataMap.get("myStateData");
state.add(new Date());
System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
}
}
</pre>
通過context.getMergedJobDataMap()獲取,然后回去對應的state值,然后向state中添加值。或者通過注射,在job類中定義對應的setXxx()實現:
<pre>
public class DumbJob implements Job {
String jobSays;
float myFloatValue;
ArrayList state;
public DumbJob() { }
public void execute(JobExecutionContext context)throws JobExecutionException{
JobKey key = context.getJobDetail().getKey();
JobDataMap dataMap = context.getMergedJobDataMap(); // Note the difference from the previous example
state.add(new Date());
System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
}
public void setJobSays(String jobSays) {
this.jobSays = jobSays;
}
public void setMyFloatValue(float myFloatValue) {
myFloatValue = myFloatValue;
}
public void setState(ArrayList state) {
state = state;
}
}
</pre>

Trigger

trigger對象用來觸發任務執行。當需要調度一個任務時候,需要實例化一個trigger,并且調整參數來給調度器使用。所有的Trigger類型都有TriggerKey屬性,用來追蹤確定它們的身份。還有一些其它通用參數通過TriggerBuilder構建trigger過程中指定的。
jobKey:用來表明當trigger觸發執行時要執行的任務。
startTime:用來表明觸發器什么時候開始觸發執行。這個值是java.util.Date對象定義的時間。
endTime:用來表明調度觸發器什么時候失效。
trigger有多種觸發類型,比較常用的有SimpleTrigger和CronTrigger:

SimpleTrigger

當一個任務在一個特定時刻執行一次,或在特定時刻時刻重復執行,可以使用SimpleTrigger。例如你想2017年1月10日 12:32:24執行,或者每十秒執行五次。SimpleTrigger中的開始時間(startTime)、結束時間(endTime)、重復次數(repeat)、重復間隔(repeat interval)。重復可以為0或者一個整數,還可以為SimpleTrigger.REPEAT_INDEFINITELY;重復間隔必須是0或者一個long型,代表毫秒數,如果設置為0將會造成重復觸發器同時執行。

CronTrigger

如果你需要基于日歷的調度,可以使用CronTirgger,CronTrigger比SimpleTrigger更常用。例如每個月的星期五、每個工作日的九點、每周一九點到十點的每五分鐘。
CronTrigger使用Cron-Expressions配置,Cron-Expressions使用以下七種表達式(Sub-Expression)表示:
1、Seconds
2、Minutes
3、Hours
4、Day-of-Month
5、Month
6、Day-of-Week
7、Year
例如:”0 0 12 ? * WED”代表每周三的12:00:00pm
每個二級表達式可以是一個列表,例如:”MON,WED,FRI”
表達式介紹:

  • 通配符“xxx”代表的是“每次xxxx時候”,“MON-WED,SAT”每周一到周三和周六。
  • ”*”:代表0次或者多次,在Day-Of-Week中代表著”一周中的每一天”。每個字符都需要有效,比如分鐘需要在059,小時需要是023,Day-Of-Month必須是131(注意月份的天數),月份是011或者使用:JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC,Day-Of-Week是1~7(1表示周日)或者是SUN, MON, TUE, WED, THU, FRI and SAT。
  • ”/“:代表值的增量,例如0/15在分鐘里(還可以用0,15,30,45) ,代表的是從0,每小時的15分鐘。/35在分鐘表達式中代表是每小時的第35分鐘,從0開始(還可以表示為0,35)。
  • “?”:用在day-of-month和day-of-week里面,用來表示不確定值。
  • “L”:用在day-of-month和day-of-week里面,在day-of-month代表每個月的最后一天比如1月的31號和4四月的30號。在day-of-week中代表每周最后一天7(周六)。也可以用在day-of-week后一個字段值,意識是每個月的最后第幾天,例如6L,每個月的最后一個星期五,也可以指定偏移量L-3代表每個月的最后第三天。L不能使用指定的列表中,否則會引起混亂。
  • “W”:代表的意思是工作日,例如15W,這個月的第15個工作日。
  • “#”:代表每個月xxx工作日,例如在day-of-week中6#3,每個月的第三個周五。

例子:
“0 0/5 * * *?” 每五分鐘
“10 0/5 * * *?”每五分鐘10秒之后
“0 30 10-13 ? * WED,FRI”代表周三周五的10:30,11:30,12:30,13:30
“0 0/30 8-9 5,20 * ?”代表的是每個月5號和20的8點到9點的每半個小時。
代碼中使用:
<pre>
Trigger trigger1 = newTrigger().withIdentity("myTrigger","group1").withSchedule(cronSchedule("0 0/2 8-17 * * ?")).forJob("myJob","group1").build();
</pre>
Quartz將任務和觸發分離,這樣有很多好處,比如:任務創建和存儲與觸發器相互獨立;許多觸發器可以觸發同一個任務;還可以任務不變的情況下修改和重新定義觸發器,這樣有效的對調度器進行了解耦合。
Jobs和Trigger還定義了key,注冊到調度器中,這樣允許將這些key放到不同的分組中,這個功能很實用于一個公司內多個部門,每個部門使用自己的組,這個key是名稱和分組的組合,并且這個key唯一。
ScheduleBuilder有多種類型

Quartz的一些主要特性就是這些,當然還有配置和API可以以后再說。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容