簡介
Quartz是什麼?
Quartz是一個特性豐富的、開源的作業排程架構。它可以內建到任何Java應用。
使用它,你可以非常輕松的實作定時任務的排程執行。
Quartz的應用場景
場景1:提醒和告警
場景2:監聽事務
場景3:定時作業
Quartz的安裝
安裝
1.可以直接在官網:
http://www.quartz-scheduler.org/下載下傳jar包。
2.如果使用maven,可以在pom.xml中添加以下依賴jar包:
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.1</version> </dependency> <artifactId>quartz-jobs</artifactId> |
源碼
Github位址:https://github.com/quartz-scheduler/quartz
Hello World範例
開始學習之前,慣例還是show一下Hello
World。
例:
1.先定義一個Job
import java.util.Date; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class HelloJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println(String.format("Hello World! Time:%s", new Date())); } } |
2.定義Job和Trigger去排程我們定義的HelloJob。
import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.SimpleScheduleBuilder; import org.quartz.Trigger; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; import org.zp.tent.scheduler.demo.job.HelloJob; /** * @Title HelloQuartz * @Description Quartz的Hello World執行個體 * @Author zhangpeng * @Date 2016年7月6日 */ HelloWorldDemo { public static void main(String[] args) { try { // 通過schedulerFactory擷取一個排程器 SchedulerFactory schedulerfactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerfactory.getScheduler(); // 建立jobDetail執行個體,綁定Job實作類 JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("helloJob", "jobGroup1").build(); // 定義排程觸發規則,本例中使用SimpleScheduleBuilder建立了一個5s執行一次的觸發器 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "triggerGroup1").startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()) .build(); // 把作業和觸發器注冊到任務排程中 scheduler.scheduleJob(jobDetail, trigger); // 啟動排程 scheduler.start(); // 60s後關閉 Thread.sleep(1000 * 30); scheduler.shutdown(); System.out.println("排程任務結束"); } catch (Exception e) { e.printStackTrace(); } |
好了,運作一下試試吧。
API
核心API
Scheduler接口:
作用:Scheduler接口是Quartz最核心的接口。Scheduler維護着JobDetail和Trigger的注冊資訊。一旦注冊成功,Scheduler負責執行和Job關聯的觸發器。
一個Scheduler執行個體可以視為一個排程作業容器。可以通過start和shutdown方法來控制它的生命周期。
// 通過schedulerFactory擷取一個排程器 SchedulerFactory schedulerfactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerfactory.getScheduler(); // 啟動 scheduler.start(); … //關閉 scheduler.shutdown(); |
Job接口
作用:開發者實作該接口定義需要執行的作業。JobExecutionContext類提供排程上下文的各種資訊。
實作Job接口的類還可以使用注解進行修飾。
@DisallowConcurrentExecution:此注解表示不允許這個Job并發執行
@PersistJobDataAfterExecution:此注解表示當這個Job的execute方法執行成功後,更新并存儲它所持有的JobDetail屬性中JobDataMap。如果使用這個注解,強烈建議也使用@DisallowConcurrentExecution,因為并發執行過程中,JobDataMap有可能會發生沖突。
public class xxxJob implements Job { … |
JobDetail接口
作用:用于定義Job執行個體。
JobDetail有兩個boolean屬性。
isDurable:如果設為false,則對應的Job一旦沒有關聯的觸發器,就會被Scheduler自動删除。
requestsRecovery:如果設為true,當Job執行中遇到硬中斷(例如運作崩潰、機器斷電等),Scheduler會重新執行。這種情況下,JobExecutionContext.isRecovering()會傳回ture。
JobBuilder類
作用:用于定義、建構JobDetail執行個體。
// 建立jobDetail執行個體,綁定Job實作類 JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("helloJob", "jobGroup1").build(); |
Trigger接口
作用:定義Job執行的觸發規則。
Quartz中有多種觸發器,最常用的是SimpleTrigger 和 CronTrigger。
SimpleTrigger一般用于隻執行一次或在指定時間執行的作業;CronTrigger一般用于周期性執行(例如,每日執行、每周執行)的作業,需要按照指定的時間表達式規則設定排程時間。
Priority:這個屬性表示Trigger的權重。當兩個Trigger觸發時間相同時,權重大的那個先執行。Quartz預設的權重值為5。
Misfire Instruction:在Trigger接口中可以設定錯過觸發處理機制。就是說在指定觸發的時間點由于某種原因錯過執行的時機了,這時如何去處理。Quartz提供了多種政策,這裡不詳述,有興趣的可以參考官方文檔。
Job和Trigger的關系
多個Job可以依賴于一個Trigger;多個Trigger也可以關聯一個Job。
但是,從最佳實踐來看,最好讓Job和Trigger保持一對多的關系,這樣更便于管理。
TriggerBuilder類
作用:用于定義、建構Trigger執行個體。
下面兩種方式是一樣的效果,都是建立一個每5s執行一次的觸發器
// 定義排程觸發規則, SimpleScheduleBuilder方式 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "triggerGroup1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build(); // 定義排程觸發規則, CronScheduleBuilder方式 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "triggerGroup1").startNow().withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")).build(); |
第二種觸發器建構方式中使用了形如"0/5 * * * * ?"的CronExpression表達式來建立觸發器規則。這裡不在細說,在下文的CronExpression表達式一節再詳述。
JobDataMap
JobDetail接口中持有JobDataMap類。開發者可以将作業執行時需要的參數或對象填入這個類中。
填入資料和擷取資料的方式很類似Json。
先定義一個Job
WithJobDataMapJob implements Job { // 基本資訊 JobKey jobKey = context.getJobDetail().getKey(); TriggerKey triggerKey = context.getTrigger().getKey(); // 擷取JobDataMap的方式:如果是基本類型,JobDataMap提供了多種get方法;如果是引用類型,可以直接get,然後進行強制轉換 JobDataMap dataMap = context.getJobDetail().getJobDataMap(); Student student = (Student) dataMap.get("student"); List<String> interests = (List<String>) dataMap.get("interests"); String word = dataMap.getString("word"); System.out.println(String.format("[JobKey:%s][TriggerKey:%s] of DumbJob print info:", jobKey, triggerKey)); System.out.println(String.format("[Student]name:%s, age:%d, sex:%s", student.getName(), student.getAge(), student.getSex())); StringBuilder interestsStr = new StringBuilder(); for (String item : interests) { interestsStr.append(item + " "); System.out.println("His interests ars: " + interestsStr.toString()); System.out.println("He want to say: " + word); System.out.println("==================================="); |
用戶端代碼:
public static void main(String[] args) { JobBuilder.newJob(WithJobDataMapJob.class).withIdentity("myJob", "group1").build(); // 使用JobDataMap填入想要攜帶的特殊資訊。可以填入基本資料類型、字元串、集合,甚至是一個對象。填入方式很類似JSON Student student = new Student("Jack", 20, "male"); List<String> interests = new ArrayList<String>(); interests.add("dancing"); interests.add("singing"); interests.add("swimming"); String word = "Hello World!"; JobDataMap map = jobDetail.getJobDataMap(); map.put("student", student); map.put("interests", interests); map.put("word", word); |
其他常見API
JobKey 和 TriggerKey
在Quartz中,可以分别通過JobKey和TriggerKey來唯一地識别一個Job或一個Trigger。
這兩個Key都有兩個關鍵屬性:name和group。
CronExpression表達式
還記得上文中展示的使用CronScheduleBuilder方式建構觸發器時的例子嗎?在這個例子中,我們使用的表達式字元串"0/5
* * * * ?"是什麼意思呢?閱讀本節後,你就會了解了。
表達式規則
一個cron表達式有至少6個(也可能7個)有空格分隔的時間元素。
CronTrigger配置完整格式為: [秒] [分] [小時] [日] [月] [周] [年]
參數設定規則見下表
字段 | 允許值 | 允許的特殊字元 |
秒 | 0-59 | , - * / |
分 | ||
小時 | 0-23 | |
日期 | 1-31 | , - * ? / L W |
月份 | 1-12 或者 JAN-DEC | |
星期 | 1-7 或者 SUN-SAT | , - * ? / L # |
年(可選) | 留白, 1970-2099 |
表 cronExpression表達式參數
符号說明
通配符*
表示所有值。
例如:在分的字段上設定 "*",表示每一分鐘都會觸發。
通配符?
表示不指定值。使用的場景為不需要關心目前設定這個字段的值。
例如:要在每月的10号觸發一個操作,但不關心是周幾,是以需要周位置的那個字段設定為"?"
具體設定為 0 0 0 10 * ?
通配符-
表示區間。
例如在小時上設定 "10-12",表示 10,11,12點都會觸發。
通配符,
表示指定多個值。
例如在周字段上設定 "MON,WED,FRI" 表示周一,周三和周五觸發
通配符/
用于遞增觸發。如在秒上面設定"5/15" 表示從5秒開始,每增15秒觸發(5,20,35,50)。在月字段上設定'1/3'所示每月1号開始,每隔三天觸發一次。
通配符L
表示最後的意思。
例如在日字段設定上,表示當月的最後一天(依據目前月份,如果是二月還會依據是否是潤年[leap]), 在周字段上表示星期六,相當于"7"或"SAT"。如果在"L"前加上數字,則表示該資料的最後一個。例如在周字段上設定"6L"這樣的格式,則表示“本月最後一個星期五"
通配符W
表示離指定日期的最近那個工作日(周一至周五)。
例如在日字段上設定"15W",表示離每月15号最近的那個工作日觸發。如果15号正好是周六,則找最近的周五(14号)觸發, 如果15号是周未,則找最近的下周一(16号)觸發。如果15号正好在工作日(周一至周五),則就在該天觸發。如果指定格式為 "1W",它則表示每月1号往後最近的工作日觸發。如果1号正是周六,則将在3号下周一觸發。(注,"W"前隻能設定具體的數字,不允許區間"-")。
小提示:'L'和
'W'可以一組合使用。如果在日字段上設定"LW",則表示在本月的最後一個工作日觸發;周字段的設定,若使用英文字母是不區分大小寫的,即MON與mon相同。
通配符#
表示每月的第幾個周幾。
例如在周字段上設定"6#3"表示在每月的第三個周六。注意如果指定"#5",正好第五周沒有周六,則不會觸發該配置(用在母親節和父親節再合适不過了)。
注:表中月份一行的JAN-DEC,是指一月到十二月的英文縮寫;星期一行的SUN-SAT,是指星期天到星期六的英文縮寫。
使用表達式的案例
案例 | 意義 |
"0 0 12 * * ?" | 每天中午12點觸發 |
"0 15 10 ? * *" | 每天上午10:15觸發 |
"0 15 10 * * ?" | |
"0 15 10 * * ? *" | |
"0 15 10 * * ? 2005" | 2005年的每天上午10:15 觸發 |
"0 * 14 * * ?" | 在每天下午2點到下午2:59期間的每1分鐘觸發 |
"0 0/5 14 * * ?" | 在每天下午2點到下午2:55期間的每5分鐘觸發 |
"0 0/5 14,18 * * ?" | 在每天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發 |
"0 0-5 14 * * ?" | 在每天下午2點到下午2:05期間的每1分鐘觸發 |
"0 10,44 14 ? 3 WED" | 每年三月的星期三的下午2:10和2:44觸發 |
"0 15 10 ? * MON-FRI" | 周一至周五的上午10:15觸發 |
"0 15 10 15 * ?" | 每月15日上午10:15觸發 |
"0 15 10 L * ?" | 每月最後一日的上午10:15觸發 |
"0 15 10 ? * 6L" | 每月的最後一個星期五上午10:15觸發 |
"0 15 10 ? * 6L 2002-2005" | 2002年至2005年的每月的最後一個星期五上午10:15觸發 |
"0 15 10 ? * 6#3" | 每月的第三個星期五上午10:15觸發 |
參考資料
官方文檔:
http://www.quartz-scheduler.org/documentation/官方2.2版本教程:
http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/