quartz 注解 mysql_SpringBoot+Quartz+MySQL实现分布式定时任务

第一步:引入依赖

org.quartz-scheduler

quartz

2.3.0

org.quartz-scheduler

quartz-jobs

2.3.0

org.springframework

spring-context-support

第二步:创建MySQL表,Quartz是基于表来感知其他定时任务节点的,节点间不会直接通信。建表语句在jar包中自带了。

org\quartz-scheduler\quartz\2.3.0\quartz-2.3.0.jar!\org\quartz\impl\jdbcjobstore\tables_mysql_innodb.sql

quartz 注解 mysql_SpringBoot+Quartz+MySQL实现分布式定时任务_第1张图片

第三步:配置线程池,我这里是因为项目的其他地方有用到线程池,你也可以选择在Quartz的配置类中注入。

(我在其他位置使用了线程池,占用了一个线程,所以当我将核心线程数量设置为1时,定时任务不会执行;需确保有足够的线程来执行)

importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;importjava.util.concurrent.Executor;importjava.util.concurrent.ThreadPoolExecutor;/*** @Author 1

* @Description 配置线程池交给Spring容器管理

* @Date 2020/8/26 18:23

**/@Configurationpublic classExecturConfig {

@Bean("taskExector")publicExecutor taskExector() {

ThreadPoolTaskExecutor executor= newThreadPoolTaskExecutor();//核心线程池数量

executor.setCorePoolSize(2);//最大线程数量

executor.setMaxPoolSize(5);//线程池的队列容量

executor.setQueueCapacity(10);//线程名称的前缀

executor.setThreadNamePrefix("expireOrderHandle-");//配置拒绝策略

executor.setRejectedExecutionHandler(newThreadPoolExecutor.AbortPolicy());

executor.initialize();returnexecutor;

}

}

第四步:因为定时任务业务中需要使用到注入Spring容器的类,所以配置注入,否则报空指针异常。

importorg.quartz.spi.TriggerFiredBundle;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.beans.factory.config.AutowireCapableBeanFactory;importorg.springframework.scheduling.quartz.AdaptableJobFactory;importorg.springframework.stereotype.Component;

@Component("myAdaptableJobFactory")public class MyAdaptableJobFactory extendsAdaptableJobFactory {//AutowireCapableBeanFactory 可以将一个对象添加到SpringIOC容器中,并且完成该对象注入

@AutowiredprivateAutowireCapableBeanFactory autowireCapableBeanFactory;/*** 该方法需要将实例化的任务对象手动的添加到springIOC容器中并且完成对象的注入*/@Overrideprotected Object createJobInstance(TriggerFiredBundle bundle) throwsException {

Object obj= super.createJobInstance(bundle);//将obj对象添加Spring IOC容器中,并完成注入

this.autowireCapableBeanFactory.autowireBean(obj);returnobj;

}

}

第五步:添加Quartz属性文件

#============================================================================# Configure JobStore

# Using Spring datasource in SchedulerConfig.java

# Spring uses LocalDataSourceJobStore extension of JobStoreCMT

#============================================================================#设置为TRUE不会出现序列化非字符串类到 BLOB 时产生的类版本问题

org.quartz.jobStore.useProperties=true#quartz相关数据表前缀名

org.quartz.jobStore.tablePrefix =QRTZ_

#开启分布式部署

org.quartz.jobStore.isClustered = true#分布式节点有效性检查时间间隔,单位:毫秒

org.quartz.jobStore.clusterCheckinInterval = 20000#信息保存时间 默认值60秒

org.quartz.jobStore.misfireThreshold = 60000#事务隔离级别为“读已提交”

org.quartz.jobStore.txIsolationLevelReadCommitted = true#配置线程池实现类

org.quartz.jobStore.class =org.quartz.impl.jdbcjobstore.JobStoreTX

org.quartz.jobStore.driverDelegateClass =org.quartz.impl.jdbcjobstore.StdJDBCDelegate

#============================================================================# Configure Main Scheduler Properties

# Needed to manage cluster instances

#============================================================================org.quartz.scheduler.instanceName =ClusterQuartz

org.quartz.scheduler.instanceId=AUTO

#如果你想quartz-scheduler出口本身通过RMI作为服务器,然后设置“出口”标志true(默认值为false)。

org.quartz.scheduler.rmi.export = false#true:链接远程服务调度(客户端),这个也要指定registryhost和registryport,默认为false

# 如果export和proxy同时指定为true,则export的设置将被忽略

org.quartz.scheduler.rmi.proxy = falseorg.quartz.scheduler.wrapJobExecutionInUserTransaction = false#============================================================================# Configure ThreadPool

# Can also be configured in spring configuration

#============================================================================#线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求)

#org.quartz.threadPool.class =org.quartz.simpl.SimpleThreadPool

#org.quartz.threadPool.threadCount = 5#org.quartz.threadPool.threadPriority = 5#org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

第六步:写任务类

packagecom.website.task;importcom.website.mapper.WebTeacherMapper;importcom.website.pojo.ao.TeacherSalaryRuleAO;importcom.website.pojo.bo.TeacherSalaryRuleDetailBO;importcom.website.pojo.bo.TeacherSalaryRuleRelationBO;importcom.website.pojo.bo.TeacherSalaryStatTempBO;importcom.website.pojo.bo.TeacherSalaryStatisticBO;importio.jsonwebtoken.lang.Collections;importlombok.extern.slf4j.Slf4j;importorg.apache.commons.lang3.StringUtils;importorg.joda.time.DateTime;importorg.quartz.DisallowConcurrentExecution;importorg.quartz.JobExecutionContext;importorg.quartz.JobExecutionException;importorg.quartz.PersistJobDataAfterExecution;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.scheduling.quartz.QuartzJobBean;importjava.math.BigDecimal;importjava.util.ArrayList;importjava.util.HashSet;importjava.util.List;importjava.util.Map;importjava.util.stream.Collectors;

@PersistJobDataAfterExecution

@DisallowConcurrentExecution

@Slf4jpublic class QuartzJob extendsQuartzJobBean {

@AutowiredprivateWebTeacherMapper webTeacherMapper;

@Overrideprotected void executeInternal(JobExecutionContext context) throwsJobExecutionException {

log.info("统计老师月薪资定时任务开始执行。");System.out.println("任务编写位置");log.info("统计老师月薪资定时任务执行完毕。");

}}

第七步:配置定时器

importcom.website.task.QuartzJob;importorg.quartz.Scheduler;importorg.quartz.TriggerKey;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.beans.factory.config.PropertiesFactoryBean;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.core.io.ClassPathResource;importorg.springframework.scheduling.quartz.CronTriggerFactoryBean;importorg.springframework.scheduling.quartz.JobDetailFactoryBean;importorg.springframework.scheduling.quartz.SchedulerFactoryBean;importjavax.sql.DataSource;importjava.io.IOException;importjava.util.Properties;importjava.util.concurrent.Executor;

@Configurationpublic classSchedulerConfig {

@AutowiredprivateDataSource dataSource;

@AutowiredprivateExecutor taskExector;

@AutowiredprivateMyAdaptableJobFactory myAdaptableJobFactory;

@Beanpublic Scheduler scheduler() throwsException {

Scheduler scheduler=schedulerFactoryBean().getScheduler();

TriggerKey triggerKey1= TriggerKey.triggerKey("trigger1", "TriggerTest111");/*========如果有必要可以配置删除任务,开始====================*/

//停止触发器//scheduler.pauseTrigger(triggerKey1);//移除触发器//scheduler.unscheduleJob(triggerKey1);//JobKey jobKey1 = JobKey.jobKey("job1111------", "quartzTest--------");//删除任务//boolean b = scheduler.deleteJob(jobKey1);//System.out.println(b);

/*=========结束====================*/scheduler.start();returnscheduler;

}

@Beanpublic SchedulerFactoryBean schedulerFactoryBean() throwsIOException {

SchedulerFactoryBean factory= newSchedulerFactoryBean();//开启更新job

factory.setOverwriteExistingJobs(true);//如果不配置就会使用quartz.properties中的instanceName//factory.setSchedulerName("Cluster_Scheduler");//配置数据源,这是quartz使用的表的数据库存放位置

factory.setDataSource(dataSource);//设置实例在spring容器中的key

factory.setApplicationContextSchedulerContextKey("applicationContext");//配置线程池

factory.setTaskExecutor(taskExector);//配置配置文件

factory.setQuartzProperties(quartzProperties());//设置调度器自动运行

factory.setAutoStartup(true);//配置任务执行规则,参数是一个可变数组

factory.setTriggers(trigger1().getObject());//解决mapper无法注入问题,此处配合第四步的配置。

factory.setJobFactory(myAdaptableJobFactory);returnfactory;

}

@Beanpublic Properties quartzProperties() throwsIOException {

PropertiesFactoryBean propertiesFactoryBean= newPropertiesFactoryBean();

propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));//在quartz.properties中的属性被读取并注入后再初始化对象

propertiesFactoryBean.afterPropertiesSet();returnpropertiesFactoryBean.getObject();

}

@BeanpublicJobDetailFactoryBean job1() {

JobDetailFactoryBean jobDetail= newJobDetailFactoryBean();//配置任务的具体实现

jobDetail.setJobClass(QuartzJob.class);//是否持久化

jobDetail.setDurability(true);//出现异常是否重新执行

jobDetail.setRequestsRecovery(true);//配置定时任务信息

jobDetail.setName("TeacherSalaryJob");

jobDetail.setGroup("TeacherSalaryJobGroup");

jobDetail.setDescription("这是每月1号凌晨统计教师薪资任务");returnjobDetail;

}

@BeanpublicCronTriggerFactoryBean trigger1() {

CronTriggerFactoryBean cronTrigger= newCronTriggerFactoryBean();//定时规则的分组

cronTrigger.setGroup("TeacherSalaryTriggerGroup");

cronTrigger.setName("TeacherSalaryTrigger");//配置执行的任务jobdetail

cronTrigger.setJobDetail(job1().getObject());//配置执行规则 每月一号0点过1分执行一次

cronTrigger.setCronExpression("0 1 0 1 * ? ");returncronTrigger;

}

}

到此完毕,另外发现如果执行任务的代码中报错,会导致定时任务停止循环,重启也不会再执行。建议任务内容用try...catch代码块包裹起来,打印好日志。

已中断的任务清空Quartz所有表格,再启动项目即可再次触发启动任务。

如果某一天定时任务突然不执行了,网上很多情况都是远程调用没有加超时中断,从而导致线程阻塞引起的。

抛异常中断周期执行原因暂未明确,有知道的大佬还请不吝赐教。

你可能感兴趣的:(quartz,注解,mysql)