1.准备工作,构建一个maven web工程
pom.xml如下:
<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.test.quartz</groupId>
<artifactId>test-quartz</artifactId>
<version>0.0.1</version>
<packaging>jar</packaging>
<name>test-quartz</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.27</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.8</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<!-- Transaction dependency is required with Quartz integration -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
<!-- Quartz framework -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
</dependencies>
<build>
<finalName>shaketv-game-quartz</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
有了工程之后还得有个有数据库,数据库的sql文件可以到官网下载
地址:http://www.quartz-scheduler.org/downloads/
解压后找到docs/dbTables,里面有各种数据库的sql文件,我这里选的是mysql
2.定义一个Job Model
public class ScheduleJob implements Serializable{
private static final long serialVersionUID = 1L;
private String jobId;
private String jobName;
private String jobGroup;
private String jobStatus;
private String cronExpression;
private Date startTime;
private String desc;
private Object data;
//set get .....
}
3.建一个Job
import java.util.Date;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
//这里注解的意思是 任务是否可以并发 加上注解表示不可以并发
@DisallowConcurrentExecution
public class MyJob implements Job{
@Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
//在任务里 可以通过getJobDataMap()传自己需要用的一些数据
JobDataMap jdm = context.getJobDetail().getJobDataMap();
//TODO
System.out.println(jdm.get("data"));
System.out.println("strat" + (new Date().getTime()));
}
}
4.定义一个管理任务的类,里面包括增、删、改、查、暂停、恢复等方法
注:一般任务里使用的trigger有两种SimpleTrigger 和CronTrigger
import java.util.List;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.ScheduleBuilder;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.quartz.impl.triggers.SimpleTriggerImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class JobService {
static ApplicationContext context = new ClassPathXmlApplicationContext(
"spring-main.xml");
static Scheduler scheduler = (Scheduler) context.getBean("scheduler");
/**
* 添加或修改任务
* @param job
* @param jobClass
* @throws SchedulerException
*/
public static void addOrUpdateJob(ScheduleJob job, Class<? extends Job> jobClass)
throws SchedulerException {
TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(),job.getJobGroup());
// 获取trigger
//CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
SimpleTriggerImpl trigger = (SimpleTriggerImpl) scheduler.getTrigger(triggerKey);
// 不存在,创建一个
if (null == trigger) {
System.out.println("new");
JobDetail jobDetail = JobBuilder.newJob(jobClass)
.withIdentity(job.getJobName(), job.getJobGroup()).build();
jobDetail.getJobDataMap().put("data", job.getData());
// 表达式调度构建器
// CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
// .cronSchedule(job.getCronExpression());
// 按新的cronExpression表达式构建一个新的trigger
// trigger = TriggerBuilder.newTrigger()
// .withIdentity(job.getJobName(), job.getJobGroup())
// .withSchedule(scheduleBuilder).build();
ScheduleBuilder<?> schedBuilder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(R.TIME_INTERVAL).withRepeatCount(-1);
trigger = (SimpleTriggerImpl) TriggerBuilder.newTrigger()
.withIdentity(job.getJobName(), job.getJobGroup()).startAt(job.getStartTime()).withSchedule(schedBuilder).build();
scheduler.scheduleJob(jobDetail, trigger);
} else {
System.out.println("reset");
// Trigger已存在,那么更新相应的定时设置
// 表达式调度构建器
// CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
// .cronSchedule(job.getCronExpression());
// 按新的cronExpression表达式重新构建trigger
// trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
// .withSchedule(scheduleBuilder).build();
ScheduleBuilder<?> schedBuilder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(R.TIME_INTERVAL).withRepeatCount(-1);
trigger = (SimpleTriggerImpl) TriggerBuilder.newTrigger()
.withIdentity(job.getJobName(), job.getJobGroup()).startAt(job.getStartTime()).withSchedule(schedBuilder).build();
// 按新的trigger重新设置job执行
JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
scheduler.getJobDetail(jobKey).getJobDataMap().put("data", job.getData());
scheduler.rescheduleJob(triggerKey, trigger);
}
}
/**
* 暂停任务
* @param job
* @throws SchedulerException
*/
public static void pauseJob(ScheduleJob job) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
scheduler.pauseJob(jobKey);
}
/**
* 恢复单个任务
* @param job
* @throws SchedulerException
*/
public static void recoveryJob(ScheduleJob job) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
scheduler.resumeJob(jobKey);
}
/**
* 全部恢复
* @param job
* @throws SchedulerException
*/
public static void resumeAll() throws SchedulerException{
scheduler.resumeAll();
}
/**
* 查询任务信息
* @throws SchedulerException
*/
public static void queryJob() throws SchedulerException {
for (String groupName : scheduler.getJobGroupNames()) {
for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName))) {
String jobName = jobKey.getName();
String jobGroup = jobKey.getGroup();
List<Trigger> triggers = (List<Trigger>) scheduler.getTriggersOfJob(jobKey);
Trigger trigger = triggers.get(0);
System.out.println("[jobName] : " + jobName
+ " [groupName] : "+ jobGroup
+ " [nextFireTime]: " + trigger.getNextFireTime()
);
}
}
}
/**
* 删除任务
* @param job
* @throws SchedulerException
*/
public static void delelteJob(ScheduleJob job) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
scheduler.deleteJob(jobKey);
}
/**
* 同时删除任务和定时器
* @param job
* @throws SchedulerException
*/
public static void delelteJobAndTrigger(ScheduleJob job) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(),job.getJobGroup());
scheduler.pauseTrigger(triggerKey);
scheduler.unscheduleJob(triggerKey);
scheduler.deleteJob(jobKey);
}
/**
* 立即执行
* @param job
* @throws SchedulerException
*/
public static void triggerNow(ScheduleJob job) throws SchedulerException{
JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
scheduler.triggerJob(jobKey);
}
/**
* 立即执行
* @param job
* @throws SchedulerException
*/
public static void triggerNow(ScheduleJob job,JobDataMap data) throws SchedulerException{
JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
scheduler.triggerJob(jobKey,data);
}
}
5.spring配置:spring-main.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-4.0.xsd">
<context:annotation-config />
<bean id="DataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/quartz" />
<property name="username" value="root" />
<property name="password" value="12345678" />
<!-- 连接池最大使用连接数量 -->
<property name="maxActive" value="100" />
<!-- 初始化大小 -->
<property name="initialSize" value="10" />
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="60000" />
<!-- 连接池最小空闲 -->
<property name="minIdle" value="5" />
<!-- 逐出连接的检测时间间隔 -->
<property name="timeBetweenEvictionRunsMillis" value="3000" />
<!-- 最小逐出时间 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
<!-- 测试有效用的SQL Query -->
<property name="validationQuery" value="SELECT 'x'" />
<!-- 连接空闲时测试是否有效 -->
<property name="testWhileIdle" value="true" />
<!-- 获取连接时测试是否有效 -->
<property name="testOnBorrow" value="false" />
<!-- 归还连接时是否测试有效 -->
<property name="testOnReturn" value="false" />
</bean>
<!-- 声明工厂 -->
<bean id="scheduler" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource" ref="DataSource" />
<property name="configLocation" value="classpath:quartz.properties" />
<!--applicationContextSchedulerContextKey:
是org.springframework.scheduling.quartz.SchedulerFactoryBean这个类中
把spring上下 文以key/value的方式存放在了quartz的上下文中了,
可以用applicationContextSchedulerContextKey所定义的key得到对应的spring上下文-->
<property name="applicationContextSchedulerContextKey" value="applicationContextKey"/>
</bean>
</beans>
6.quartz配置 quartz.properties
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX #job存储方式
org.quartz.jobStore.misfireThreshold = 60000 #超时时间
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10 #线程数量
org.quartz.threadPool.threadPriority = 5 #优先线程数量
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
##orgorg.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
#orgorg.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#orgorg.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.HSQLDBDelegate
#orgorg.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#org.quartz.jobStore.useProperties = false
#org.quartz.jobStore.tablePrefix = QRTZ_
#org.quartz.jobStore.isClustered = false
#org.quartz.jobStore.maxMisfiresToHandleAtATime=1
更多的配置文件信息可以进官网查看:
http://www.quartz-scheduler.org/documentation/quartz-2.2.x/configuration/
7.最后就可以写一个测试类进行测试了
import org.quartz.SchedulerException;
public class Test {
public static void getJobDataFromQueue(){
ScheduleJob job = new ScheduleJob();
job.setJobId("10001" );
job.setJobName("test1");
job.setJobGroup("test-group");
//时间可以自己设置
job.setStartTime(new Date());
job.setData("这里是传入job的数据");
try {
JobService.addOrUpdateJob(job, MyJob.class);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
getJobDataFromQueue();
}
}
测试时遇到的问题:
1.在测试过程中循环建了1000个任务,然后同时执行,发现有漏掉任务,这个可以通过调整线程数量(org.quartz.threadPool.threadCount)和超时时间(org.quartz.jobStore.misfireThreshold)来解决
2.SimpleTrigger和CronTrigger之间不能互转,开始建了一个任务使用的是CronTrigger,任务信息保存到数据库中,然后下次启动服务的时候想修改成SimpleTrigger,会报错,这种情况只能先删除该任务,然后重建。
3.不能创建已过期的任务,比如现在是2017-1-1 14:30:00,不能创建这个时间之前的任务
4.时间间隔这种CronTrigger不能很好的处理,使用SimpleTrigger,比如从现在开始隔90分钟执行一次,CronTrigger没法实现