深入解讀Quartz任務調度器
1.Quartz簡介
1.1.概要
Quartz是OpenSymphony提供的強大的開源任務調度框架。
官網:http://www.quartz-scheduler.org
純Java實現,精細控制排程。
1.2.Quartz特點
- 強大的調度能力
- 靈活的應用方式
- 強大的分布式和集群能力
1.3.Quartz設計模式
- Builder模式
- 組件模式
- Factory模式
- 鏈式寫法
1.4.Quartz體系結構
1.4.1.三大核心
- 調度器
- 任務
- 觸發器
1.4.2.重要組成
1)任務:
-
Job:表示一個工作,要執行的具體內容。此接口中只有一個方法。要創建一個任務,必須得實現這個接口。該接口只有一個execute方法,任務每次被調用的時候都會執行這個execute方法的邏輯,類似TimerTask的run方法,在里面編寫業務邏輯。
public class TestJob implements Job { /**把要執行的操作,寫在execute方法中 */ @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); System.out.println("I can do something..."); System.out.println(sdf.format(new Date())); } }
生命周期:在每次調度器執行job時,它在調用execute方法前會創建一個新的job實例,當調用完成之后,關聯的job對象實例會被釋放,釋放的實例會被垃圾回收機制回收。
-
JobBuilder:可向任務傳遞數據,通常情況下,我們使用它就可向任務類發送數據了,如有特別復雜的傳遞參數,它提供了一個傳遞遞:JobDataMap對象的方法
JobDetail jobDetail = JobBuilder.newJob(TestJob.class).withIdentity("testJob","group1").build();
JobDetail:用來保存我們任務的詳細信息。一個JobDetail可以有多個Trigger,但是一個Trigger只能對應一個JobDetail。下面是JobDetail的一些常用的屬性和含義:
- JobStore:負責跟蹤所有你給scheduler的“工作數據”:jobs, triggers, calendars, 等。
-
RAMJobStore:是使用最簡單的也是最高效(依據CPU時間)的JobStore 。RAMJobStore 正如它名字描述的一樣,它保存數據在RAM。缺點是你的應用結束之后所有的數據也丟失了--這意味著RAMJobStore 不具有保持job和trigger持久的能力。對于一些程序是可以接受的,甚至是期望的,但對于其他的程序可能是災難性的。使用RAMJobStore配置Quartz:配置如下
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
-
JDBCJobStore:以JDBC的方式保存數據在數據庫中。它比RAMJobStore的配置復雜一點,也沒有RAMJobStore快。然而,性能缺點不是糟透了,特別是如果你在數據庫表主鍵上建立了索引。在機器之間的LAN(在scheduler 和數據庫之間)合理的情況下,檢索和更新一個被觸發的Trigger花費的時間少于10毫秒。幾乎適用于所有的數據庫,廣泛用于 Oracle。PostgreSQL, MySQL, MS SQLServer, HSQLDB, 和DB2。使用JDBCJobStore之前你必須首先創建一系列Quartz要使用的表。你可以發現表創建語句在Quartz發布目錄的 “docs/dbTables”下面。你需要確定你的應用要使用的事務類型。如果你不想綁定調度命令(例如增加和移除Trigger)到其他的事務,你可以使用JobStoreTX (最常用的選擇)作為你的Jobstore。如果你需要Quartz和其他的事務(例如在J2EE應用服務器中)一起工作,你應該使用JobStoreCMT ,Quartz 將讓應用服務器容器管理這個事務。使用JobStoreTx配置Quartz:
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate #配置表的前綴 org.quartz.jobStore.tablePrefix = QRTZ_ #使用JNDI數據源的時候,數據源的名字 org.quartz.jobStore.dataSource = myDS
-
TerracottaJobStore:提供了一個方法:在不使用數據庫的情況下使它具有收縮性和強壯性。可以是集群的也可以是非集群的,在這兩種情況下為你的job數據提供了一個存儲機制用于應用程序重啟之間持久,因為數據是存儲在Terracotta服務器。它的性能比使用數據庫訪問JDBCJobStore好一點兒(大約是一個數量級),但是明顯比RAMJobStore慢。使用TerracottaJobStore配置Quartz:
org.quartz.jobStore.class = org.terracotta.quartz.TerracottaJobStore org.quartz.jobStore.tcConfigUrl = localhost:9510
-
- JobDataMap:中可以包含不限量的(序列化的)數據對象,在job實例執行的時候,可以使用其中的數據;JobDataMap是Java Map接口的一個實現,額外增加了一些便于存取基本類型的數據的方法。
-
存:
JobDetail jobDetail = JobBuilder.newJob(TestJob.class).withIdentity("testJob","group1").usingJobData("date1","存內容").build();
-
取:
public class TestJob implements Job { /**把要執行的操作,寫在execute方法中 */ @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { JobKey key = jobExecutionContext.getJobDetail().getKey(); JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap(); String date1 = jobDataMap.getString("date1"); } }
-
2)觸發器:用來觸發執行Job
2.1)觸發器通用屬性:
- Jobkey:表示job實例的標識,觸發器被觸發時,該指定的job實例會被執行
- StartTime:表示觸發器的時間表首次被觸發的時間,它的值類型為:java.util.Date
- EndTime:指定觸發器的不再被觸發的時間,它的值類型為:java.util.Date
2.2)觸發器類型:
-
SimpleTrigger: 主要是針對一些相對簡單的時間觸發進行配置使用,比如在指定的時間開始然后在一定的時間間隔之內重復執行一個Job,同時可以任意指定重復的次數。下面就是使用一個SimpleTrigger的例子:
//創建觸發器 每3秒鐘執行一次(無開始時間和結束時間) Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group3") .withSchedule( SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(3).repeatForever()).build(); //創建觸發器 每3秒鐘執行一次(有開始時間和結束時間) long now = new Date().getTime(); Date start = new Date(now+6000); Date end = new Date(now+12000); //創建觸發器 每3秒鐘執行一次 Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group3") .startAt(start) .endAt(end) .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever()).build();
SimpleTrigger具有豐富的構造函數,根據業務需求構造不同的構造函數。
- CronTrigger: 可以配置更復雜的觸發時刻表,基于日歷的作業觸發器,而不像SimpleTrigger那樣精確指定間隔時間,比SimpleTrigger更加常用。
Cron表達式:用于配置CronTrigger實例,是由7個表達式組成的字符串,描述了時間表的詳細信息。
格式為:[秒][分][時][日][月][周][年]
Cron表達式特殊字符意義對應表:
通配符說明:
Cron表達式例子:
TriggerBuilder.newTrigger().withIdentity("trigger2","group2")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 ? * 6L *")).build();
Cron表達式小技巧:
1. ‘L’和‘W’可以一起組合使用
2. 周字段英文字母不區分大小寫即MOM與mom相同
3. 利用工具,在線生成cron表達式:http://cron.qqe2.com/
-
NthIncludedDayTrigger:是 Quartz 開發團隊最新加入到框架中的一個 Trigger。它設計用于在每一間隔類型的第幾天執行 Job。例如,你要在每個月的 15 號執行開票的 Job,用 NthIncludedDayTrigger就再合適不過了。
NthIncludedDayTrigger trigger = new NthIncludedDayTrigger("NthIncludedDayTrigger",Scheduler.DEFAULT_GROUP); trigger.setN(15); trigger.setIntervalType(NthIncludedDayTrigger.INTERVAL_TYPE_MONTHLY);
3)調度器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,以鍵值對的方式維護這些上下文數據,SchedulerContext為保存和獲取數據提供了多個put()和getXxx()的方法。可以通過Scheduler# getContext()獲取對應的SchedulerContext實例;
SchedulerFactory schedulerfactory=new StdSchedulerFactory();
Scheduler scheduler = schedulerfactory.getScheduler();
DirectSchedulerFactory factory = DirectSchedulerFactory.getInstance();
try {
Scheduler scheduler = factory.getScheduler();
} catch (SchedulerException e) {
e.printStackTrace();
}
4)SchedulerFactory:
- 使用一組參數(java.util.Properties)來創建和出書啊Quartz調度器
- 配置參數一般存儲在quartz.properties中
- 調用getScheduler方法就能創建和初始化調度器
5)quartz.properties:
Quartz-Job的quartz.properties配置文件說明,此文件在quartz的jar包有,可直接拿過來使用不過只有基本的幾個配置 自己可根據需要進行擴充;另外如果項目中沒有對該配置文件重寫,則Quartz會加載自己jar包中的quartz.properties文件。
# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#
# ===========================================================================
# Configure Main Scheduler Properties 調度器屬性
# ===========================================================================
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
#org.quartz.scheduler.instanceid:AUTO
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
# ===========================================================================
# Configure ThreadPool 線程池屬性
# ===========================================================================
#線程池的實現類(一般使用SimpleThreadPool即可滿足幾乎所有用戶的需求)
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
#指定線程數,至少為1(無默認值)(一般設置為1-100直接的整數合適)
org.quartz.threadPool.threadCount: 10
#設置線程的優先級(最大為java.lang.Thread.MAX_PRIORITY 10,最小為Thread.MIN_PRIORITY 1,默認為5)
org.quartz.threadPool.threadPriority: 5
#設置SimpleThreadPool的一些屬性
#設置是否為守護線程
#org.quartz.threadpool.makethreadsdaemons = false
#org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
#org.quartz.threadpool.threadsinheritgroupofinitializingthread=false
#線程前綴默認值是:[Scheduler Name]_Worker
#org.quartz.threadpool.threadnameprefix=swhJobThead;
# 配置全局監聽(TriggerListener,JobListener) 則應用程序可以接收和執行 預定的事件通知
# ===========================================================================
# Configuring a Global TriggerListener 配置全局的Trigger監聽器
# MyTriggerListenerClass 類必須有一個無參數的構造函數,和 屬性的set方法,目前2.2.x只支持原始數據類型的值(包括字符串)
# ===========================================================================
#org.quartz.triggerListener.NAME.class = com.swh.MyTriggerListenerClass
#org.quartz.triggerListener.NAME.propName = propValue
#org.quartz.triggerListener.NAME.prop2Name = prop2Value
# ===========================================================================
# Configuring a Global JobListener 配置全局的Job監聽器
# MyJobListenerClass 類必須有一個無參數的構造函數,和 屬性的set方法,目前2.2.x只支持原始數據類型的值(包括字符串)
# ===========================================================================
#org.quartz.jobListener.NAME.class = com.swh.MyJobListenerClass
#org.quartz.jobListener.NAME.propName = propValue
#org.quartz.jobListener.NAME.prop2Name = prop2Value
# ===========================================================================
# Configure JobStore 存儲調度信息(工作,觸發器和日歷等)
# ===========================================================================
# 信息保存時間 默認值60秒
org.quartz.jobStore.misfireThreshold: 60000
#保存job和Trigger的狀態信息到內存中的類
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
# ===========================================================================
# Configure SchedulerPlugins 插件屬性 配置
# ===========================================================================
# 自定義插件
#org.quartz.plugin.NAME.class = com.swh.MyPluginClass
#org.quartz.plugin.NAME.propName = propValue
#org.quartz.plugin.NAME.prop2Name = prop2Value
#配置trigger執行歷史日志(可以看到類的文檔和參數列表)
org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingTriggerHistoryPlugin
org.quartz.plugin.triggHistory.triggerFiredMessage = Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss MM/dd/yyyy}
org.quartz.plugin.triggHistory.triggerCompleteMessage = Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy} with resulting trigger instruction code: {9}
#配置job調度插件 quartz_jobs(jobs and triggers內容)的XML文檔
#加載 Job 和 Trigger 信息的類 (1.8之前用:org.quartz.plugins.xml.JobInitializationPlugin)
org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin
#指定存放調度器(Job 和 Trigger)信息的xml文件,默認是classpath下quartz_jobs.xml
org.quartz.plugin.jobInitializer.fileNames = my_quartz_job2.xml
#org.quartz.plugin.jobInitializer.overWriteExistingJobs = false
org.quartz.plugin.jobInitializer.failOnFileNotFound = true
#自動掃描任務單并發現改動的時間間隔,單位為秒
org.quartz.plugin.jobInitializer.scanInterval = 10
#覆蓋任務調度器中同名的jobDetail,避免只修改了CronExpression所造成的不能重新生效情況
org.quartz.plugin.jobInitializer.wrapInUserTransaction = false
# ===========================================================================
# Sample configuration of ShutdownHookPlugin ShutdownHookPlugin插件的配置樣例
# ===========================================================================
#org.quartz.plugin.shutdownhook.class = \org.quartz.plugins.management.ShutdownHookPlugin
#org.quartz.plugin.shutdownhook.cleanShutdown = true
#
# Configure RMI Settings 遠程服務調用配置
#
#如果你想quartz-scheduler出口本身通過RMI作為服務器,然后設置“出口”標志true(默認值為false)。
#org.quartz.scheduler.rmi.export = false
#主機上rmi注冊表(默認值localhost)
#org.quartz.scheduler.rmi.registryhost = localhost
#注冊監聽端口號(默認值1099)
#org.quartz.scheduler.rmi.registryport = 1099
#創建rmi注冊,false/never:如果你已經有一個在運行或不想進行創建注冊
# true/as_needed:第一次嘗試使用現有的注冊,然后再回來進行創建
# always:先進行創建一個注冊,然后再使用回來使用注冊
#org.quartz.scheduler.rmi.createregistry = never
#Quartz Scheduler服務端端口,默認是隨機分配RMI注冊表
#org.quartz.scheduler.rmi.serverport = 1098
#true:鏈接遠程服務調度(客戶端),這個也要指定registryhost和registryport,默認為false
# 如果export和proxy同時指定為true,則export的設置將被忽略
#org.quartz.scheduler.rmi.proxy = false