(一)Quartz單機
1.Quartz簡介
Quartz是一個完全由java編寫的開源作業調度框架,能實現業務的定時調度。Quartz主要有三個核心調度器、任務和觸發器:
①任務-JobDetail:Quartz在任務調度的時候,需要創建一個任務實例,JobDetail正是承擔這個角色。在Quartz2.0以前的版本中,創建一個任務通過new JobDetail(String jobName, String gruop, Class jobCLass)這個方法來創建,在2.0之後的版本中通過JobBuilder來創建任務。Job接口中只有一個方法void execute(JobExecutionContext context) throws JobExecutionException,因此在任務調度時,只需要實現execute方法就可以了,使用起來很方便。
②觸發器-Trigger:定義Job的執行時間、次數等信息,有SimpleTrigger和CronTrigger兩種類型。當你需要的是一次性的調度,或者你需要在指定的時間激活某個任務並執行N次,設置每次任務執行的間隔時間T。那此時使用SimpleTrigger將是非常方便的。如果你想在每周的周一7:00或者每月的第2,3天實現任務調度,這時候就需要用到CronTrigger,CronTrigger能夠提供復雜的觸發器表達式滿足我們的需求。同時需要提到的一點是Quartz還提供很多日期的實現類DateBuilder等,在實際使用中很方便。
③調度器-Scheduler:Quartz框架的核心是調度器。調度器負責管理Quartz應用運行時環境。調度器不是靠自己做所有的工作,而是依賴框架內一些非常重要的部件。Quartz不僅僅是線程和線程管理。為確保可伸縮性,Quartz采用了基於多線程的架構。啟動時,框架初始化一套worker線程,這套線程被調度器用來執行預定的作業。這就是Quartz怎樣能並發運行多個作業的原理。Quartz依賴一套松耦合的線程池管理部件來管理線程環境。
2.搭建Quartz工程
①創建一個新工程
新建文件lib作為外部jar包,Quartz最新版本通過官網可以下載http://www.quartz-scheduler.org/downloads/。導入你下載的quartz包,新建package為com.example.singleQuartz,裡面就兩個簡單的類SingleQuartzJob和SingleQuartzServer。SingleQuartzJob定義Job的實現類,SingleQuartzServer任務調度服務。

②SingleQuartzJob.java定義Job的實現類
package com.example.singleQuartz;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
/**
* Created by XuHui on 2016/12/22.
*/
public class SingleQuartzJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.print("Hello, Quartz! - executing its JOB at "+
new Date() + " by " + context.getTrigger().getJobKey() + "\n");
}
}
③SingleQuartzServer.java實現任務調度

package com.example.singleQuartz;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* Created by XuHui on 2016/12/22.
*/
public class SingleQuartzServer {
public static void main(String[] args) throws SchedulerException {
/* 獲取Scheduler實例 */
SchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();
/* 啟動調度器 */
scheduler.start();
/* 任務:withIdentity(String name, String group),其中groupName可以自己定義,也可以用Scheduler提供的DEFAULT_GROUP。
這裡要求同組裡面的jobName不能相同*/
JobDetail jobDetail = JobBuilder.newJob(SingleQuartzJob.class)
.withIdentity("job", Scheduler.DEFAULT_GROUP)
.build();
/*觸發器:SimpleTrigger和CronTrigger。實現的功能是:接下來30s後執行job,以後每個10s執行一次,重復10次,一共執行11次。
nextGivenSecondDate(null, 30)距離現在時間30s之後執行job,此處null可寫作new Date(),可自行在api查看源碼實現 */
Date startTime = DateBuilder.nextGivenSecondDate(null, 30);
SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger()
.withIdentity("trigger", Scheduler.DEFAULT_GROUP)
.startAt(startTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(10)
.withRepeatCount(10))
.forJob(jobDetail)
.build();
/* 交由調度器調度Job */
scheduler.scheduleJob(jobDetail, simpleTrigger);
/* 3分鐘任務調度之後關閉調度器 */
try{
TimeUnit.MINUTES.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
scheduler.shutdown();
}
}
}
View Code
④執行任務調度
執行run->main但是發現報錯了。此時是少了兩個個日志包,slf4j-api-1.7.22.jar和slf4j-simple-1.7.22.jar,我們只要添加slf4j包就可以了。
通過官網可以下載http://www.slf4j.org/download.html,找到這兩個包放到lib下就可以了。
Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
at org.quartz.impl.StdSchedulerFactory.<init>(StdSchedulerFactory.java:303)
at com.example.singleQuartz.SingleQuartzServer.main(SingleQuartzServer.java:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.ClassNotFoundException: org.slf4j.LoggerFactory
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 7 more
解決完上面日志包的問題,我們運行上面程序可以看到正常的調度結果如下。
[main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started. Hello, Quartz! - executing its JOB at Thu Dec 22 19:40:30 CST 2016 by DEFAULT.job Hello, Quartz! - executing its JOB at Thu Dec 22 19:40:40 CST 2016 by DEFAULT.job Hello, Quartz! - executing its JOB at Thu Dec 22 19:40:50 CST 2016 by DEFAULT.job Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:00 CST 2016 by DEFAULT.job Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:10 CST 2016 by DEFAULT.job Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:20 CST 2016 by DEFAULT.job Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:30 CST 2016 by DEFAULT.job Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:40 CST 2016 by DEFAULT.job Hello, Quartz! - executing its JOB at Thu Dec 22 19:41:50 CST 2016 by DEFAULT.job Hello, Quartz! - executing its JOB at Thu Dec 22 19:42:00 CST 2016 by DEFAULT.job Hello, Quartz! - executing its JOB at Thu Dec 22 19:42:10 CST 2016 by DEFAULT.job [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutting down. [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED paused. [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutdown complete.
⑤任務調度的多線程
Scheduler是存在多對多的關系,由於線程池的存在,調度器實現多線程並發執行任務調度,直接看下面demo就能明白。

package com.example.singleQuartz;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* Created by XuHui on 2016/12/22.
*/
public class SingleQuartzThreadPoolServer {
public static void main(String[] args) throws SchedulerException {
/* 獲取Scheduler實例 */
SchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();
/* 啟動調度器 */
scheduler.start();
/******************************************************* Job-1 ****************************************************************/
/* 任務:withIdentity(String name, String group),其中groupName可以自己定義,也可以用Scheduler提供的DEFAULT_GROUP。
這裡要求同組裡面的jobName不能相同*/
JobDetail jobDetail = JobBuilder.newJob(SingleQuartzJob.class)
.withIdentity("job1", Scheduler.DEFAULT_GROUP)
.build();
/*觸發器:SimpleTrigger和CronTrigger。實現的功能是:接下來30s後執行job,以後每個10s執行一次,重復10次,一共執行11次。
nextGivenSecondDate(null, 30)距離現在時間30s之後執行job,此處null可寫作new Date(),可自行在api查看源碼實現 */
Date startTime = DateBuilder.nextGivenSecondDate(null, 30);
SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", Scheduler.DEFAULT_GROUP)
.startAt(startTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(10)
.withRepeatCount(2))
.forJob(jobDetail)
.build();
/* 交由調度器調度Job */
scheduler.scheduleJob(jobDetail, simpleTrigger);
/******************************************************* Job-2 ****************************************************************/
jobDetail = JobBuilder.newJob(SingleQuartzJob.class)
.withIdentity("job2", Scheduler.DEFAULT_GROUP)
.build();
simpleTrigger = TriggerBuilder.newTrigger()
.withIdentity("trigger2", Scheduler.DEFAULT_GROUP)
.startAt(startTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(10)
.withRepeatCount(2))
.forJob(jobDetail)
.build();
scheduler.scheduleJob(jobDetail, simpleTrigger);
/******************************************************* Job-3 ****************************************************************/
jobDetail = JobBuilder.newJob(SingleQuartzJob.class)
.withIdentity("job3", Scheduler.DEFAULT_GROUP)
.build();
simpleTrigger = TriggerBuilder.newTrigger()
.withIdentity("trigger3", Scheduler.DEFAULT_GROUP)
.startAt(startTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(10)
.withRepeatCount(2))
.forJob(jobDetail)
.build();
scheduler.scheduleJob(jobDetail, simpleTrigger);
/* 2分鐘任務調度之後關閉調度器 */
try{
TimeUnit.MINUTES.sleep(2);
} catch (Exception e) {
e.printStackTrace();
} finally {
scheduler.shutdown();
}
}
}
View Code
執行結果如下:
[main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started. Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:30 CST 2016 by DEFAULT.job1 Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:30 CST 2016 by DEFAULT.job2 Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:30 CST 2016 by DEFAULT.job3 Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:40 CST 2016 by DEFAULT.job3 Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:40 CST 2016 by DEFAULT.job1 Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:40 CST 2016 by DEFAULT.job2 Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:50 CST 2016 by DEFAULT.job1 Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:50 CST 2016 by DEFAULT.job3 Hello, Quartz! - executing its JOB at Thu Dec 22 19:52:50 CST 2016 by DEFAULT.job2 [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutting down. [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED paused. [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutdown complete.
(二)任務調度持久到數據庫
1.建立數據庫和建表
上面的例子的任務是存在內存中的,如果程序關閉任務就會消失,為了保持任務的可持久性,需要將任務調度存到數據庫中。在你下載的quartz包下找到quartz-2.2.1\docs\dbTables\,找到你對應的數據庫建表語言,這裡我用的是mysql數據庫tables_mysql.sql。在你本地或者服務器數據庫新建一個數據庫quartz(你可以不建,主要為了和其他數據庫區分)。將以下的建表語言導入該數據庫。

#
# Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar
#
# PLEASE consider using mysql with innodb tables to avoid locking issues
#
# In your Quartz properties file, you'll need to set
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;
CREATE TABLE QRTZ_JOB_DETAILS
(
SCHED_NAME VARCHAR(120) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME VARCHAR(250) NOT NULL,
IS_DURABLE VARCHAR(1) NOT NULL,
IS_NONCONCURRENT VARCHAR(1) NOT NULL,
IS_UPDATE_DATA VARCHAR(1) NOT NULL,
REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);
CREATE TABLE QRTZ_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
NEXT_FIRE_TIME BIGINT(13) NULL,
PREV_FIRE_TIME BIGINT(13) NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16) NOT NULL,
TRIGGER_TYPE VARCHAR(8) NOT NULL,
START_TIME BIGINT(13) NOT NULL,
END_TIME BIGINT(13) NULL,
CALENDAR_NAME VARCHAR(200) NULL,
MISFIRE_INSTR SMALLINT(2) NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);
CREATE TABLE QRTZ_SIMPLE_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
REPEAT_COUNT BIGINT(7) NOT NULL,
REPEAT_INTERVAL BIGINT(12) NOT NULL,
TIMES_TRIGGERED BIGINT(10) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE QRTZ_CRON_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
CRON_EXPRESSION VARCHAR(200) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE QRTZ_SIMPROP_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
STR_PROP_1 VARCHAR(512) NULL,
STR_PROP_2 VARCHAR(512) NULL,
STR_PROP_3 VARCHAR(512) NULL,
INT_PROP_1 INT NULL,
INT_PROP_2 INT NULL,
LONG_PROP_1 BIGINT NULL,
LONG_PROP_2 BIGINT NULL,
DEC_PROP_1 NUMERIC(13,4) NULL,
DEC_PROP_2 NUMERIC(13,4) NULL,
BOOL_PROP_1 VARCHAR(1) NULL,
BOOL_PROP_2 VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE QRTZ_BLOB_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
BLOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE QRTZ_CALENDARS
(
SCHED_NAME VARCHAR(120) NOT NULL,
CALENDAR_NAME VARCHAR(200) NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);
CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);
CREATE TABLE QRTZ_FIRED_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
ENTRY_ID VARCHAR(95) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
FIRED_TIME BIGINT(13) NOT NULL,
SCHED_TIME BIGINT(13) NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR(16) NOT NULL,
JOB_NAME VARCHAR(200) NULL,
JOB_GROUP VARCHAR(200) NULL,
IS_NONCONCURRENT VARCHAR(1) NULL,
REQUESTS_RECOVERY VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);
CREATE TABLE QRTZ_SCHEDULER_STATE
(
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
CHECKIN_INTERVAL BIGINT(13) NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);
CREATE TABLE QRTZ_LOCKS
(
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);
commit;
View Code
2.導入mysql數據庫的jdbc驅動包,mysql-connector-java-5.1.40-bin.jar
3.配置Quartz配置文件quartz.properities
org.quartz.scheduler.instanceName = MyScheduler org.quartz.threadPool.threadCount = 3 org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.tablePrefix = QRTZ_ org.quartz.jobStore.dataSource = myDS org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz?characterEncoding=utf-8 org.quartz.dataSource.myDS.user = root org.quartz.dataSource.myDS.password = 123456 org.quartz.dataSource.myDS.maxConnections = 5
4.任務調度的持久性
其實介紹這部分主要是為了後面的Quartz集群做鋪墊。為了驗證任務調度的可持久性,我們從這兩方面驗證,當我們程序停止運行的時候,查看數據庫能查看到任務調度的數據;當重新啟動同一調度器的時候,原來的任務能繼續執行。SingleQuartzKeepAlive.java如下,其實和上面的SingleQuartzServer基本一樣,只是加載了Quartz配置文件。

package com.example.singleQuartz;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* Created by XuHui on 2016/12/22.
*/
public class SingleQuartzKeepAlive {
public static void main(String[] args) throws SchedulerException {
/* 獲取Scheduler實例 */
StdSchedulerFactory factory = new StdSchedulerFactory();
factory.initialize("quartz.properities");
Scheduler scheduler = factory.getScheduler();
/* 啟動調度器 */
scheduler.start();
JobDetail jobDetail = JobBuilder.newJob(SingleQuartzJob.class)
.withIdentity("job", Scheduler.DEFAULT_GROUP)
.build();
Date startTime = DateBuilder.nextGivenSecondDate(null, 30);
SimpleTrigger simpleTrigger = (SimpleTrigger) TriggerBuilder.newTrigger()
.withIdentity("trigger", Scheduler.DEFAULT_GROUP)
.startAt(startTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(10)
.withRepeatCount(10))
.forJob(jobDetail)
.build();
/* 交由調度器調度Job */
scheduler.scheduleJob(jobDetail, simpleTrigger);
/* 3分鐘任務調度之後關閉調度器 */
try{
TimeUnit.MINUTES.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
scheduler.shutdown();
}
}
}
View Code
我們run->main發現報錯了,報錯如下。這個因為我們缺少了c3p0-0.9.1.2.jar,下載地址為http://www.mchange.com/projects/c3p0/index.html。將這個包加入lib中即可。

Exception in thread "main" java.lang.NoClassDefFoundError: com/mchange/v2/c3p0/ComboPooledDataSource
at org.quartz.utils.PoolingConnectionProvider.initialize(PoolingConnectionProvider.java:210)
at org.quartz.utils.PoolingConnectionProvider.<init>(PoolingConnectionProvider.java:155)
at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:1014)
at org.quartz.impl.StdSchedulerFactory.getScheduler(StdSchedulerFactory.java:1519)
at com.example.singleQuartz.SingleQuartzKeepAlive.main(SingleQuartzKeepAlive.java:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.ClassNotFoundException: com.mchange.v2.c3p0.ComboPooledDataSource
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 10 more
Process finished with exit code 1
View Code
運行結果正常,我們在數據庫可以看到job和trigger表都有了任務調度記錄數據。此時我們不妨只啟動原來的調度器,看看是否任務調度還能正常進行。通過結果同學們就很清楚了,這次任務調度我們只執行了4次,因為在程序中斷之前運行了7次。

package com.example.singleQuartz;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;
import java.util.concurrent.TimeUnit;
/**
* Created by XuHui on 2016/12/23.
*/
public class SingleQuartzOldScheduler {
public static void main(String[] args) throws SchedulerException {
StdSchedulerFactory factory = new StdSchedulerFactory();
factory.initialize("quartz.properities");
Scheduler scheduler = factory.getScheduler();
scheduler.start();
try{
TimeUnit.MINUTES.sleep(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
scheduler.shutdown();
}
}
}
=================結果====================
[main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED started.
Hello, Quartz! - executing its JOB at Fri Dec 23 09:20:16 CST 2016 by DEFAULT.job
Hello, Quartz! - executing its JOB at Fri Dec 23 09:20:26 CST 2016 by DEFAULT.job
Hello, Quartz! - executing its JOB at Fri Dec 23 09:20:36 CST 2016 by DEFAULT.job
Hello, Quartz! - executing its JOB at Fri Dec 23 09:20:46 CST 2016 by DEFAULT.job
[main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED shutting down.
[main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED paused.
[main] INFO org.quartz.core.QuartzScheduler - Scheduler MyScheduler_$_NON_CLUSTERED shutdown complete.
View Code
5.最終的工程目錄結構

(三)Quartz集群
1.搭建web工程
看完第二部分之後,大部分同學已經明白了,Quartz集群是怎麼回事了。其實就是把調度任務存到數據庫中,集群的時候每台服務器調度任務都指向同一數據庫,從同一數據庫取調度任務,這就是Quartz集群了。為在本地驗證quartz集群,我重新搭建一個工程,裡面撘兩個web模塊,目錄結構如下。clusterweb_two和clusterweb_one文件和配置一模一樣。

2.配置Quartz集群文件quartz.properities
多台服務器上的這個配置文件除了instanceId不同,這裡可以設置成AUTO根據機器自動生成,其他基本都相同,必須限定的是數據庫信息必須相同。
org.quartz.scheduler.instanceName = ClusterScheduler org.quartz.threadPool.threadCount = 3 org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.tablePrefix = QRTZ_ org.quartz.jobStore.dataSource = myDS # Cluster org.quartz.jobStore.isClustered = true org.quartz.scheduler.instanceId = AUTO org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz?characterEncoding=utf-8 org.quartz.dataSource.myDS.user = root org.quartz.dataSource.myDS.password = 123456 org.quartz.dataSource.myDS.maxConnections = 5
3.配置web監聽器
我們把調度器放到web監聽器中運行,當web啟動時會運行這個監聽器,同時會啟動調度器,監聽器QuartzApplicationListener.java如下所示。

package com.example.web;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* Created by XuHui on 2016/12/23.
*/
public class QuartzApplicationListener implements ServletContextListener {
private Scheduler scheduler = null;
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
StdSchedulerFactory factory = new StdSchedulerFactory();
try {
factory.initialize("quartz.properities");
scheduler = factory.getScheduler();
/* 啟動調度器 */
scheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("job", Scheduler.DEFAULT_GROUP)
.build();
Date startTime = DateBuilder.nextGivenSecondDate(null, 30);
SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger()
.withIdentity("trigger", Scheduler.DEFAULT_GROUP)
.startAt(startTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(10)
.withRepeatCount(10))
.forJob(jobDetail)
.build();
try{
/* 交由調度器調度Job */
scheduler.scheduleJob(jobDetail, simpleTrigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
/* 3分鐘任務調度之後關閉調度器 */
try{
TimeUnit.MINUTES.sleep(3);
}catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
try{
scheduler.shutdown();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
View Code
4.web配置文件web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<listener>
<listener-class>
com.example.web.QuartzApplicationListener
</listener-class>
</listener>
</web-app>
5.運行兩個tomcat模擬集群
在idea中配置兩個tomcat,並且讓兩個tomcat同時運行有點小麻煩,主要注意一點每個tomcat配置的Http Port 和 JMX Port要不同。
tomcat1 : Http Port : 8081 JMX Port : 1099 tomcat2 : Http Port : 8082 JMX Port : 1100
tomcat1和tomcat2運行成功之後,此時兩個服務都已啟動,此時只有tomcat1開始任務調度,tomcat2未進行任務調度。首先把tomcat1服務停掉,此時會出現tomcat2任務調度開始,這裡跟做服務高可靠性很像。運行結果:
==============tomcat1===================== Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:00 CST 2016 by DEFAULT.job on webone Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:10 CST 2016 by DEFAULT.job on webone Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:20 CST 2016 by DEFAULT.job on webone Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:30 CST 2016 by DEFAULT.job on webone D:\work\tomcat\apache-tomcat-8.0.23-windows-x64\apache-tomcat-8.0.23\bin\catalina.bat stop Disconnected from server ==============tomcat2===================== Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:56 CST 2016 by DEFAULT.job on webtwo Hello, Quartz! - executing its JOB at Fri Dec 23 11:27:56 CST 2016 by DEFAULT.job on webtwo Hello, Quartz! - executing its JOB at Fri Dec 23 11:28:00 CST 2016 by DEFAULT.job on webtwo Hello, Quartz! - executing its JOB at Fri Dec 23 11:28:10 CST 2016 by DEFAULT.job on webtwo Hello, Quartz! - executing its JOB at Fri Dec 23 11:28:20 CST 2016 by DEFAULT.job on webtwo Hello, Quartz! - executing its JOB at Fri Dec 23 11:28:30 CST 2016 by DEFAULT.job on webtwo Hello, Quartz! - executing its JOB at Fri Dec 23 11:28:40 CST 2016 by DEFAULT.job on webtwo [2016-12-23 11:29:48,369] Artifact clusterweb_two:war exploded: Artifact is deployed successfully
寫在最後:Quartz基礎部分總結起來可能不多,實際應用主要是Job中execute方法的實現。看完上面的介紹之後我們發現,Quartz使用起來非常方便,Quartz提供了很多日期和日歷類。寫過定時備份、定時查詢統計方法的同學,知道這裡寫起來很麻煩。Quqatz大大縮減了程序的代碼量。之前和一位同事討論了什麼時候用Quartz,很多時候我們都會自己寫任務調度。一般都是這樣實現的:我們會啟線程相當於定時器,定時去查詢符合條件的Job,如果查詢到多Job同時調度,我們會啟一個線程池多線程並發運行。這樣看上去Quartz都具備了,使用Quartz之後代碼非常簡潔、可復用性高並且任務調度的可持久性,只需要配置Quartz配置文件就可以將調度任務存到數據庫中,正是因為這點所以Quartz可以集群,對於任務數很多的集群環境下,這就是無與倫比的優點了。如果在搭建環境或者發現文章中有不對或不足的地方,可以在下方留言,大家共同學習。