定時任務調度工具之Quartz(一)
一、Quartz介紹
- OpenSymphony提供的強大的開源任務調度框架;
- 官網:http://www.quartz-scheduler.org/
- 純Java實現,精細控制排程;
1.特點
(1)強大的調度功能:作為spring默認的調度框架,很容易與spring集成,實現靈活可配置的調度功能; 還提供了調度運行環境的持久化機制,可以保存並恢復調度現場, 即使系統因故障關閉,任務調度現場數據並不會丟失。 (2)靈活的應用方式:允許開發者靈活的定義觸發器的調度時間表並可以為觸發器和任務進行關聯映射。 (3)分布式和集群能力。
2.主要用到的設計模式
Builder模式
Factory模式
組件模式
鏈式寫法
3.三個核心概念
調度器:負責定期定時定頻率的去執行任務
任務:包括了業務邏輯
觸發器:讓東西生效的時間
4.Quartz的體系結構
5.重要組成
(1)Job:
區別與JobDetail,是一個接口,只有一個方法void execute(JobExecutionContext context),
開發者可以實現該接口定義運行任務,相當於TimerTask下面的run()方法,區別在於,
Job有一個參數JobExecutionContext,JobExecutionContext這
個類提供了調度上下文的各種信息,Job運行時的信息就保存在
JobExecutionContext里的JobDataMap實例中。
(2)JobDetail:
Quartz在每次執行實例的時候都重新創建一個job實例,所以它不直接接受一個job實例,
而是通過接受一個job實現類,以便運行時通過new Instance()的反射機制實例化job,
因此需要通過一個類來描述job的實現類及其他相關靜態信息,如job的名字,描述,關聯監聽器等信息。
(即用來綁定job,並且在job執行的時候攜帶一些執行的信息)
通過該類的構造函數可以更具體地了解它的功用:
JobDetail(java.lang.String name, java.lang.String group, java.lang.Class jobClass),
該構造函數要求指定Job的實現類,以及任務在Scheduler中的組名和Job名稱;
(3)JobBuilder:
用來定義或者創建JobDetail的實例,JobDetail限定了只能是job的實例。
(4)JobStore:
接口,用來保存job數據,實現類主要有RAMJobStore,JobStoreTX,JobStoreCMT;
JobStoreTX和JobStoreCMT均將數據保存在數據庫中,
RAMJobStore將數據保存在內存中,保存一些執行的信息。
(5)Trigger:
一個類,描述觸發的job執行時的時間觸發規則;主要有SimpleTrigger和CronTrigger兩個子類。
當僅觸發一次或者以固定時間間隔周期執行時,使用SimpleTrigger;
CronTrigger通過cron表達式,定義出各種復雜時間規則的調度方案,
如每天早晨的固定時間執行,或周二周三的固定時間執行等需求。
(6)TriggerBuilder:
使用builder模式,用來定義或者創建觸發器的實例
(7)ThreadPool:
Timer有且只有一個后台線程在執行,Quartz的schedule下有ThreadPool整個線程池來運行,
schedule使用線程池作為任務運行的基礎設施,任務通過共享線程池中的線程提高運行的效率,
從而解決並發問題
(8)Scheduler:
調度器,代表Quartz的一個獨立運行容器,Trigger和JobDetail可以注冊到Scheduler中,
兩者在Scheduler中擁有各自的組及名稱,組及名稱是Scheduler查找定位容器中某一對象的依據,
Trigger的組及名稱必須唯一,
JobDetail的組和名稱也必須唯一(但可以和Trigger的組和名稱相同,因為它們是不同類型的)。
Scheduler定義了多個接口方法,允許外部通過組及名稱訪問和控制容器中Trigger和JobDetail。
Scheduler可以將Trigger綁定到某一JobDetail中,這樣當Trigger觸發時,對應的Job就被執行。
一個Job可以對應多個Trigger,但一個Trigger只能對應一個Job。
可以通過SchedulerFactory創建一個Scheduler實例。
Scheduler擁有一個SchedulerContext,它類似於ServletContext,保存着Scheduler上下文信息,
Job和Trigger都可以訪問SchedulerContext內的信息。
SchedulerContext內部通過一個Map,以鍵值對的方式維護這些上下文數據,S
chedulerContext為保存和獲取數據提供了多個put()和getXxx()的方法。
可以通過Scheduler# getContext()獲取對應的SchedulerContext實例;
(9)Calendar:
一個Trigger可以和多個Calendar關聯,以排除或包含某些時間點。
比如某個任務希望放假時間不執行。
(10)監聽器:
JobListener,TriggerListener,SchedulerListener;分別對Job,
Trigger,Scheduler的事件進行監聽,包括scheduler一運行起來的時
候,或者在執行任務的時候,或終止的時候進行監聽,監聽的時候加入一些
自定義的某些邏輯,比如打印日志信息。
二、第一個Quartz程序
需求:讓任務每2秒打印一次helloworld
pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.hcx.demo</groupId> <artifactId>HelloQuartz</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.3</version> </dependency> </dependencies> </project>
任務類:
package com.hcx.HelloQuartz; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.text.SimpleDateFormat; import java.util.Date; /** * Created by HCX on 2017/9/6. * 自定義任務 */ public class HelloJob implements Job { public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { //打印當前的執行時間,格式為:2017-09-06 00:00:00 Date date = new Date(); SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("當前的時間為(HelloJob):" + sf.format(date)); //編寫具體的業務邏輯 System.out.println("Hello Job!"); } }
任務調度類:
package com.hcx.HelloQuartz; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.SimpleScheduleBuilder; import org.quartz.Trigger; import org.quartz.TriggerBuilder; import org.quartz.impl.StdSchedulerFactory; import java.text.SimpleDateFormat; import java.util.Date; /** * Created by HCX on 2017/9/6. */ public class HelloScheduler { public static void main(String[] args) throws SchedulerException { /** * JobDetail:用來綁定Job,並且在job執行的時候攜帶一些執行的信息 */ //創建一個JobDetail實例,將該實例與HelloJob Class綁定 JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("myJob","group1").build(); /** * Trigger:用來觸發job去執行的,包括定義了什么時候去執行, * 第一次執行,是否會一直重復地執行下去,執行幾次等 */ //創建一個Trigger實例,定義該job立即執行,並且每隔2秒鍾重復執行一次,直到程序停止 /** * trigger通過builder模式來創建,TriggerBuilder.newTrigger() * withIdentity():定義一個標識符,定義了組 * startNow():定義現在開始執行, * withSchedule(SimpleScheduleBuilder.simpleSchedule():withSchedule也是builder模式創建 *.withIntervalInSeconds(2).repeatForever()):定義了執行頻度:每2秒鍾執行一次,不間斷地重復執行 * build():創建trigger */ Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("myTrigger","group1").startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(2).repeatForever()).build(); //創建scheduler實例: /** * scheduler區別於trigger和jobDetail,是通過factory模式創建的 */ //創建一個ScheduleFactory SchedulerFactory sfact = new StdSchedulerFactory(); Scheduler scheduler = sfact.getScheduler(); scheduler.start(); //打印當前時間 Date date = new Date(); SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("當前的時間為(HelloScheduler):" + sf.format(date)); //需要將jobDetail和trigger傳進去,並將jobDetail和trigger綁定在一起。 scheduler.scheduleJob(jobDetail,trigger); } }