Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。Quartz的最新版本为Quartz 2.2.1。
本次遇到的情况是 关于N多个抓取任务的调度,并且由于各任务本身是有关联性,不易改造成标准的Jobs,并考虑到不去入侵和修改原来的任务方法,故期望运用Quartz按照Cron设定的时间去执行指定类和方法。
因为需要保存数据到数据库,设计了一个JavaBean来存储数据结构如下:
public class Quartz {
/** 自动编号 */
private Long SCHEDULE_ID;
/** 任务名称 */
private String NAME;
/** 任务别名 */
private String ALIASNAME;
/** 任务分组 */
private String GROUP;
/** 任务执行类-全路径 */
private String CLASS;
/** 任务执行类-方法 */
private String METHOD;
/** 任务Cron表达式 */
private String CRON;
/** 任务描述 */
private String DESC;
private int STATUS_ID;
private Long CREATETIME;
private Long UPDATETIME;
private int CREATEUSER;
private int UPDATEUSER;
}
Quartz 将作业划分为:执行器(JobDetail)、触发器(Trigger)、监听器(Listener),简略说就是执行器决定如何执行任务,触发器决定何时执行任务,监听器决定任务到达某状态如何操作。
Quartz典型的JobDetail是实现一个Job接口,然后新建一个触发器去执行该Job。
典型的Quartz应用一:
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class HelloJob implements Job {
private static Logger _log = LoggerFactory.getLogger(HelloJob.class);
public HelloJob() {
}
public void execute(JobExecutionContext context)
throws JobExecutionException {
_log.error(" 执行任务: " + new Date());
}
}
测试类
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
import static org.quartz.DateBuilder.*;
import java.util.Date;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SimpleExample {
private static Logger log = LoggerFactory.getLogger(SimpleExample.class);
public void run() throws Exception {
// 通过SchedulerFactory获取一个调度器实例
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
Date runTime = evenMinuteDate(new Date());
// 通过过JobDetail封装HelloJob,同时指定Job在Scheduler中所属组及名称,这里,组名为group1,而名称为job1。
JobDetail job = newJob(HelloJob.class).withIdentity("job1", "group1").build();
// 创建一个SimpleTrigger实例,指定该Trigger在Scheduler中所属组及名称。
// 接着设置调度的时间规则.当前时间运行
Trigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(runTime).build();
// 注册并进行调度
sched.scheduleJob(job, trigger);
// 启动调度器
sched.start();
try {
//当前线程等待65秒
Thread.sleep(65L * 1000L);
} catch (Exception e) {
}
//调度器停止运行
sched.shutdown(true);
log.error("结束运行。。。。");
}
public static void main(String[] args) throws Exception {
SimpleExample example = new SimpleExample();
example.run();
}
}
典型的Quartz应用二:
<!-- 使用MethodInvokingJobDetailFactoryBean,任务类可以不实现Job接口,通过targetMethod指定调用方法-->
<bean id="taskJob" class="com.tyyd.dw.task.DataConversionTask"/>
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="group" value="job_work"/>
<property name="name" value="job_work_name"/>
<!--false表示等上一个任务执行完后再开启新的任务-->
<property name="concurrent" value="false"/>
<property name="targetObject">
<ref bean="taskJob"/>
</property>
<property name="targetMethod">
<value>execute</value>
</property>
</bean>
<!-- 调度触发器 -->
<bean id="myTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="name" value="work_default_name"/>
<property name="group" value="work_default"/>
<property name="jobDetail">
<ref bean="jobDetail" />
</property>
<property name="cronExpression">
<value>0/5 * * * * ?</value>
</property>
</bean>
<!-- 调度工厂 -->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="myTrigger"/>
</list>
</property>
</bean>
应用一是常见的java实现方式,没有使用Spring去管理Quartz对象,也反映Quartz可以脱离Spring独立执行。应用二是通过Spring配置文件实现执行实例化类的方法,但是把配置写在XML文件里面非常不方便修改。因此借鉴第二种方法实现Quartz的动态加载任务,同理也可实现增加、删除、暂停、启动、单次执行等功能。
考虑到Quartz调度工厂不需要多例,并且考虑注入的便捷性,在SpringMvc.xml文件里配置:
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"/>
并将其注入到SchedulerJobService中。
package cn.focus.sh.core.quartz.service;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PostConstruct;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
import org.springframework.stereotype.Service;
@Service
public class ScheduleJobService {
@Autowired
private Scheduler scheduler;
private static Map<String,Quartz> QuartzMap = new ConcurrentHashMap<String,Quartz>();
@Autowired
SpringUtils utils;
@Autowired
private QuartzDao quartzDao;
@PostConstruct
public void init(){
//从数据库初始化任务列表
List<Quartz> list = quartzDao.selectAll();
for(Quartz quartz :list){
run(quartz);
}
}
public void run(Quartz quartz){
MethodInvokingJobDetailFactoryBean bean =new MethodInvokingJobDetailFactoryBean();
try {
Class<?> loader = Class.forName(quartz.getCLASS());
bean.setTargetObject(utils.getBean(loader));
bean.setTargetClass(loader);
bean.setTargetMethod(quartz.getMETHOD());
bean.setGroup(quartz.getGROUP());
bean.setName(quartz.getNAME());
bean.setConcurrent(false);
bean.afterPropertiesSet();
//表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartz.getCRON());
//按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(quartz.getNAME(), quartz.getGROUP())
.withSchedule(scheduleBuilder).build();
try {
scheduler.scheduleJob(bean.getObject(), trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
CLASS cn.xxx.xxx.manager.xxxManager
METHOD getCount
CRON 0/10 * * * * ?
目前只实现了动态执行任务,同理可以实现动态暂停、删除、增加、修改等功能。