为什么使用Quartz
定时任务是框架不可缺少的一部分,spring自带的定时任务工具,已经可以满足使用,但无法满足分布式的情况(实际也可以用分布式锁来实现),所以使用Quartz来实现框架的定时器功能
1.Quartz的一般使用
一般框架下怎么使用
1.1 引入jar
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
1.2 定义一个Job,具体执行任务的类
@DisallowConcurrentExecution
public class HelloJob implements Job{
private static Logger log = LogManager.getLogger();
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
log.info(" HelloJob ");
}
}
1.3 定义jobDetail,trigger,Scheduler
job与jobDetail绑定,形成任务
trigger定义了任务的执行时间
Scheduler 执行一对任务与触发器
public class HelloScheduler {
public static void main(String[] args) throws SchedulerException, ParseException {
// jobdetail 和 job实例绑定
JobDetail helloJobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("helloJob", "helloGroup").build();
// 触发器 simpleTrigger(SimpleScheduleBuilder) CronTrigger
// 每5秒运行一次
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("helloTrigger", "helloGroup")
.withSchedule(CronScheduleBuilder.cronSchedule("/5 * * * * ? *"))
.build();
// 调度器
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.start();
//执行任务
scheduler.scheduleJob(helloJobDetail, trigger);
}
}
2. springBoot2 结合 Quartz
2.1 jar引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2.2 QuartzConfiguration
public class QuartzJobVo {
//任务信息
private String jobName;
private String jobGroupName;
//触发器信息
private String triggerName;
private String triggerGroupName;
private String cron;
//具体任务类
private Class<? extends Job> jobClass;
//需要被删除
private boolean needDelete = false;
/**
* Method name: QuartzJobVo<BR>
* Description: please write your description<BR>
* Remark: <BR> <BR>
*/
public QuartzJobVo() {
super();
}
public QuartzJobVo(String jobName, String jobGroupName, String triggerName, String triggerGroupName,
Class<? extends Job> jobClass, String cron) {
super();
this.jobName = jobName;
this.jobGroupName = jobGroupName;
this.triggerName = triggerName;
this.triggerGroupName = triggerGroupName;
this.cron = cron;
this.jobClass = jobClass;
}
public QuartzJobVo(String jobName, String jobGroupName, String triggerName, String triggerGroupName,
Class<? extends Job> jobClass, String cron, boolean needDelete) {
super();
this.jobName = jobName;
this.jobGroupName = jobGroupName;
this.triggerName = triggerName;
this.triggerGroupName = triggerGroupName;
this.cron = cron;
this.jobClass = jobClass;
this.needDelete = needDelete;
}
...................
}
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class HelloJob extends QuartzJobBean{
private static Logger log = LogManager.getLogger();
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
// 任务具体执行的地方
log.info("Hello world!:" + context.getJobDetail().getKey());
}
}
@Configuration
public class QuartzConfiguration {
@Autowired
private DataSource dataSource;
private static Logger log = LogManager.getLogger();
//任务集合
private final QuartzJobVo[] Jobs = {
new QuartzJobVo("hello2", "hello2", "hello2", "hello2", HelloJob.class, "/5 * * * * ? *"),
new QuartzJobVo("hello", "hello", "hello", "hello", HelloJob.class, "4,14 * * * * ? *", true)
};
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
//quartz参数
Properties prop = new Properties();
prop.put("org.quartz.scheduler.instanceName", "WIFIScheduler");
prop.put("org.quartz.scheduler.instanceId", "AUTO");
prop.put("org.quartz.scheduler.skipUpdateCheck", true);
//线程池配置
prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
prop.put("org.quartz.threadPool.threadCount", "10");
prop.put("org.quartz.threadPool.threadPriority", "5");
//JobStore配置
prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
//集群配置
prop.put("org.quartz.jobStore.isClustered", "true");
//#设置此实例“检入”*与群集的其他实例的频率(以毫秒为单位)。影响检测失败实例的速度。
prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
// #在被认为“失火”之前,调度程序将“容忍”一个Triggers将其下一个启动时间通过的毫秒数。默认值(如果您在配置中未输入此属性)为60000(60秒)
prop.put("org.quartz.jobStore.misfireThreshold", "12000");
prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
//PostgreSQL数据库,需要打开此注释
//prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
factory.setQuartzProperties(prop);
factory.setSchedulerName("WIFIScheduler");
//延时启动
factory.setStartupDelay(30);
factory.setApplicationContextSchedulerContextKey("applicationContextKey");
//设置自动启动,默认为true
factory.setAutoStartup(true);
//可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
//实际运用中,要把jobDetail,trigger 全都交给spring管理,这个属性才起作用
factory.setOverwriteExistingJobs(true);
JobDetail[] jobDetails = new JobDetail[Jobs.length];
Trigger[] triggers = new Trigger[Jobs.length];
int size = 0;
for (QuartzJobVo vo : Jobs) {
//job定义: // 任务名,任务组,任务执行类
JobDetail jobDetail = JobBuilder.newJob(vo.getJobClass())
.withIdentity(vo.getJobName(), vo.getJobGroupName())
.storeDurably()
.build();
jobDetails[size] = jobDetail;
//触发器构建
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(vo.getTriggerName(), vo.getTriggerGroupName())
.withSchedule(CronScheduleBuilder.cronSchedule(vo.getCron()))
.forJob(jobDetail)
.build();
triggers[size] = trigger;
log.info("增加了定时任务 vo={}", JSON.toJSONString(vo));
size++;
}
factory.setJobDetails(jobDetails);
factory.setTriggers(triggers);
return factory;
}
@Bean
public void deleteJob() {
Scheduler scheduler = schedulerFactoryBean().getScheduler();
for (QuartzJobVo vo : Jobs) {
if(vo.isNeedDelete()) {
//需要被删除
JobKey jobKey = JobKey.jobKey(vo.getJobName(), vo.getJobGroupName());
if(jobKey !=null) {
try {
scheduler.deleteJob(jobKey);
log.info("定时任务删除成功 vo={}", JSON.toJSONString(vo));
} catch (Exception e) {
log.error("定时任务删除出错 vo={}", JSON.toJSONString(vo), e);
try {
scheduler.pauseJob(jobKey);
} catch (Exception e2) {
log.error("定时任务暂停出错 vo={}", JSON.toJSONString(vo), e);
}
}
}else {
log.info("定时任务已经不存在quartz表中 vo={}", JSON.toJSONString(vo));
}
}
}
}
}
3. 注意
3.1 @DisallowConcurrentExecution,将该注解加到job类上,告诉Quartz不要并发地执行同一个job定义(这里指特定的job类)的多个实例,即同一个job,即是时间到了,只要上一个还在运行,新的就等待,spring的定时默认有这个机制
3.2 @PersistJobDataAfterExecution,将该注解加在job类上,告诉Quartz在成功执行了job类的execute方法后(没有发生任何异常),更新JobDetail中JobDataMap的数据,使得该job(即JobDetail)在下一次执行的时候,JobDataMap中是更新后的数据,而不是更新前的旧数据
3.3 线程池,org.quartz.threadPool.threadCount,默认10个,每一个在运行的job会占用一个线程,线程池被用完,新的任务只能排队