Quartz框架简介
Quartz 是 OpenSymphony 开源组织在任务调度领域的一个开源项目
Quartz核心概念
Scheduler:任务调度器,是实际执行任务调度的控制器。
Trigger:触发器,用于定义任务调度的时间规则,有SimpleTrigger,CronTrigger,DateIntervalTrigger和NthIncludedDayTrigger,其中CronTrigger用的比较多,本文主要介绍这种方式。CronTrigger在spring中封装在CronTriggerFactoryBean中。
Calendar:它是一些日历特定时间点的集合。一个trigger可以包含多个Calendar,以便排除或包含某些时间点。
JobDetail:用来描述Job实现类及其它相关的静态信息,如Job名字、关联监听器等信息。
Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中。
Cron表达式
cron表达式格式:
{秒数} {分钟} {小时} {日期} {月份} {星期}{年份(可为空)}
例 “0 0 12 ? * WED” 在每星期三下午12:00 执行(年份通常 省略)
每个字段的允许值
字段 允许值 允许的特殊字符
秒 0-59 , - * /
分 0-59 , - * /
小时 0-23 , - * /
日期 1-31 , - * ? / L W C
月份 1-12 或者 JAN-DEC , - * /
星期 1-7 或者 SUN-SAT , - * ? / L C #
年(可选) 留空, 1970-2099 , - * /
值的含义
Seconds (秒) :可以用数字0-59 表示,
Minutes(分) :可以用数字0-59 表示,
Hours(时) :可以用数字0-23表示,
Day-of-Month(天) :可以用数字1-31 中的任一一个值,但要注意一些特别的月份
Month(月) :可以用0-11 或用字符串 “JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC” 表示
Day-of-Week(每周):可以用数字1-7表示(1 = 星期日)或用字符口串“SUN, MON, TUE, WED, THU, FRI and SAT”表示
Spring Boot整合Quartz
1、添加依赖
<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>
2、在maven项目的resource目录下创建quartz.properties
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.scheduler.instanceId = AUTO
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
#持久化配置
org.quartz.jobStore.misfireThreshold = 50000
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#支持集群
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.useProperties = true
org.quartz.jobStore.clusterCheckinInterval = 15000
#使用weblogic连接Oracle驱动
#org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.oracle.weblogic.WebLogicOracleDelegate
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = qzDS
#数据源连接信息,quartz默认使用c3p0数据源可以被自定义数据源覆盖
org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL = jdbc:mysql://XXXX:XXX/XXXXX?useUnicode=true&characterEncoding=utf-8
org.quartz.dataSource.qzDS.user = XXX
org.quartz.dataSource.qzDS.password = XXX
org.quartz.dataSource.qzDS.maxConnections = 10
3、quartz官网下载对应版本的quartz,解压打开docs/dbTables里面有不同数据库的建表语句,在使用quartz做持久化的时候需要用到这些表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oy2EndYb-1592549360412)(http://dwiki.cmbchina.cn/download/attachments/71968334/image2019-9-20_13-10-19.png?version=1&modificationDate=1568956220000&api=v2)]
4、添加配置类,这里创建job 实例工厂,解决spring注入问题,如果使用默认会导致spring的@Autowired 无法注入问题
public class SchedulerConfig implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private JobFactory jobFactory;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("任务已经启动..."+event.getSource());
}
@Bean(name = "dataSourceQuartz")
@ConfigurationProperties(prefix = "kb.database")
public DataSource dataSource(){
DataSource dataSource = DruidDataSourceBuilder.create().build();
return dataSource;
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean(@Autowired DataSource dataSource ) throws IOException {
//获取配置属性
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
//在quartz.properties中的属性被读取并注入后再初始化对象
propertiesFactoryBean.afterPropertiesSet();
//创建SchedulerFactoryBean
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setQuartzProperties(propertiesFactoryBean.getObject());
//使用数据源,自定义数据源
factory.setDataSource(dataSource);
factory.setJobFactory(jobFactory);
factory.setWaitForJobsToCompleteOnShutdown(true);//这样当spring关闭时,会等待所有已经启动的quartz job结束后spring才能完全shutdown。
factory.setOverwriteExistingJobs(false);
factory.setStartupDelay(1);
return factory;
}
/* * 通过SchedulerFactoryBean获取Scheduler的实例 */
@Bean(name="scheduler")
public Scheduler scheduler(@Autowired DataSource dataSource) throws IOException {
return schedulerFactoryBean(dataSource).getScheduler();
}
@Bean
public QuartzInitializerListener executorListener() {
return new QuartzInitializerListener();
}
}
5、定义JobDetail
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
@Component
public class SyncJob implements Job {
@Autowired
Question2DAO question2DAO;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap data=context.getTrigger().getJobDataMap();
String invokeParam =(String) data.get("invokeParam");
System.out.println("job one param : " + invokeParam + " : " + new Date());
//在这里实现业务逻辑
try{
List<ESQuestionModel> list = question2DAO.getUpdatedQuestions(0L);
System.out.println(list);
}catch (Exception e){
e.printStackTrace();
}
}
}
6、对定时任务进行管理
@Service
public class QuartzServiceImpl implements QuartzService {
@Autowired
@Qualifier("scheduler")
private Scheduler scheduler;
/**
* 新建一个任务
*/
@Override
public String addJob(AppQuartz appQuartz) throws Exception {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = df.parse(appQuartz.getStartTime());
if (!CronExpression.isValidExpression(appQuartz.getCronExpression())) {
return "Illegal cron expression"; //表达式格式不正确
}
JobDetail jobDetail = null;
//构建job信息
if ("JobOne".equals(appQuartz.getJobGroup())) {
jobDetail = JobBuilder.newJob(SyncJob.class).withIdentity(appQuartz.getJobName(), appQuartz.getJobGroup()).build();
}
if ("JobTwo".equals(appQuartz.getJobGroup())) {
jobDetail = JobBuilder.newJob(JobTwo.class).withIdentity(appQuartz.getJobName(), appQuartz.getJobGroup()).build();
}
//表达式调度构建器(即任务执行的时间,不立即执行)
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(appQuartz.getCronExpression()).withMisfireHandlingInstructionDoNothing();
//按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(appQuartz.getJobName(), appQuartz.getJobGroup()).startAt(date)
.withSchedule(scheduleBuilder).build();
//传递参数
if (appQuartz.getInvokeParam() != null && !"".equals(appQuartz.getInvokeParam())) {
trigger.getJobDataMap().put("invokeParam", appQuartz.getInvokeParam());
}
scheduler.scheduleJob(jobDetail, trigger);
// pauseJob(appQuartz.getJobName(),appQuartz.getJobGroup());
return "success";
}
/**
* 获取Job状态 * @param jobName * @param jobGroup * @return * @throws SchedulerException
*/
public String getJobState(String jobName, String jobGroup) throws SchedulerException {
TriggerKey triggerKey = new TriggerKey(jobName, jobGroup);
return scheduler.getTriggerState(triggerKey).name();
}
//暂停所有任务
@Override
public void pauseAllJob() throws SchedulerException {
scheduler.pauseAll();
}
//暂停任务
@Override
public String pauseJob(String jobName, String jobGroup) throws SchedulerException {
JobKey jobKey = new JobKey(jobName, jobGroup);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null) {
return "fail";
} else {
scheduler.pauseJob(jobKey);
return "success";
}
}
//恢复所有任务
@Override
public void resumeAllJob() throws SchedulerException {
scheduler.resumeAll();
}
// 恢复某个任务
@Override
public String resumeJob(String jobName, String jobGroup) throws SchedulerException {
JobKey jobKey = new JobKey(jobName, jobGroup);
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null) {
return "fail";
} else {
scheduler.resumeJob(jobKey);
return "success";
}
}
//删除某个任务
@Override
public String deleteJob(AppQuartz appQuartz) throws SchedulerException {
JobKey jobKey = new JobKey(appQuartz.getJobName(), appQuartz.getJobGroup());
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (jobDetail == null) {
return "jobDetail is null";
} else if (!scheduler.checkExists(jobKey)) {
return "jobKey is not exists";
} else {
scheduler.deleteJob(jobKey);
return "success";
}
}
//修改任务
@Override
public String modifyJob(AppQuartz appQuartz) throws SchedulerException {
if (!CronExpression.isValidExpression(appQuartz.getCronExpression())) {
return "Illegal cron expression";
}
TriggerKey triggerKey = TriggerKey.triggerKey(appQuartz.getJobName(), appQuartz.getJobGroup());
JobKey jobKey = new JobKey(appQuartz.getJobName(), appQuartz.getJobGroup());
if (scheduler.checkExists(jobKey) && scheduler.checkExists(triggerKey)) {
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
//表达式调度构建器,不立即执行
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(appQuartz.getCronExpression()).withMisfireHandlingInstructionDoNothing();
//按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
.withSchedule(scheduleBuilder).build();
//修改参数
if (!trigger.getJobDataMap().get("invokeParam").equals(appQuartz.getInvokeParam())) {
trigger.getJobDataMap().put("invokeParam", appQuartz.getInvokeParam());
}
//按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);
return "success";
} else {
return "job or trigger not exists";
}
}
}