几种任务调度
Timer,简单无门槛,一般也没人用。
ScheduledExecutorService主要用于一些单进程的简单的任务调度。
spring @Scheduled注解,一般集成于项目中,小任务很方便。
开源工具 Quartz,分布式集群开源工具,以下两个分布式任务应该都是基于Quartz实现的,可以说是中小型公司必选,当然也视自身需求而定。
分布式任务 XXL-JOB,是一个轻量级分布式任务调度框架,支持通过 Web 页面对任务进行 CRUD 操作,支持动态修改任务状态、暂停/恢复任务,以及终止运行中任务,支持在线配置调度任务入参和在线查看调度结果。
分布式任务 Elastic-Job,是一个分布式调度解决方案,由两个相互独立的子项目 Elastic-Job-Lite 和 Elastic-Job-Cloud 组成。定位为轻量级无中心化解决方案,使用 jar 包的形式提供分布式任务的协调服务。支持分布式调度协调、弹性扩容缩容、失效转移、错过执行作业重触发、并行调度、自诊。
分布式任务 Saturn,Saturn是唯品会在github开源的一款分布式任务调度产品。它是基于当当elastic-job来开发的,其上完善了一些功能和添加了一些新的feature。目前在github上开源大半年,470个star。Saturn的任务可以用多种语言开发比如python、Go、Shell、Java、Php。其在唯品会内部已经发部署350+个节点,每天任务调度4000多万次。同时,管理和统计也是它的亮点。
quartz
springboot 1.0时代
引入依赖
<!--quartz相关依赖-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>${quartz.version}</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>${quartz.version}</version>
</dependency>
配置
配置类
@Configuration
public class JobScheduleConfig {
@Autowired
private ApplicationContext applicationContext;
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setOverwriteExistingJobs(true);
factory.setAutoStartup(true);
//设置线程池
factory.setTaskExecutor(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2,
new ThreadFactoryBuilder().setNameFormat("job-schedule-factory-thread-%d").build()));
// factory.setQuartzProperties(quartzProperties());
AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
factory.setJobFactory(jobFactory);
return factory;
}
factory类
public final class AutoWiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
beanFactory = applicationContext.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
job factory
@Component
public class SchedulerJobFactory {
public JobDetailFactoryBean job(Class<? extends QuartzJobBean> jobClass, JobDataMap jobMap, String jobName) {
JobDetailFactoryBean jBean = createJobDetail(jobClass);
jBean.setJobDataMap(jobMap);
jBean.setBeanName(jobName);
jBean.afterPropertiesSet();
return jBean;
}
private JobDetailFactoryBean createJobDetail(Class<? extends QuartzJobBean> jobClass) {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(jobClass);
factoryBean.setDurability(true);
factoryBean.setRequestsRecovery(true);
factoryBean.setBeanName(jobClass.getName());
return factoryBean;
}
}
trigger factory
@Component
public class SchedulerTriggerFactory {
public CronTriggerFactoryBean jobTrigger(JobDetail jobDetail, String frequency, String triggerName) throws ParseException {
CronTriggerFactoryBean tBean = createCronTrigger(jobDetail, frequency);
tBean.setBeanName(triggerName);
tBean.afterPropertiesSet();
return tBean;
}
private CronTriggerFactoryBean createCronTrigger(JobDetail jobDetail, String cronExpression) {
CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
factoryBean.setJobDetail(jobDetail);
factoryBean.setCronExpression(cronExpression);
factoryBean.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW);
return factoryBean;
}
}
service:
@Service
public class SchedulerJobService {
private static final Logger LOGGER = LoggerFactory.getLogger(SchedulerJobService.class);
@Autowired
private SchedulerFactoryBean schedulerFactoryBean;
@Autowired
private SchedulerJobFactory schedulerJobFactory;
@Autowired
private SchedulerTriggerFactory schedulerTriggerFactory;
@Resource
private ApplicationContext applicationContext;
@PostConstruct
public void setupJobs() throws ParseException, SchedulerException {
Map<String, AStandardBaseJob> beanMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext,
AStandardBaseJob.class);
LOGGER.info("【task扫描注册已启动】-共找到task数为[{}]", beanMap != null ? beanMap.size() : 0);
beanMap.keySet().forEach(key -> {
AStandardBaseJob aStandardBaseJob = beanMap.get(key);
String className = aStandardBaseJob.getClass().getName();
LOGGER.info("[------------扫描到的任务的名称为:" + className + "----------------------]");
});
if (beanMap != null && beanMap.size() > 0) {
beanMap.forEach((key, value) -> {
if (!this.baseCheck(value)) {
return;
}
try {
scheduleJob(value.getClass(), null, value.initCronValue(), StringBuilderHolder.getGlobal().
append(value.initAppId()).append("-").append(value.initCronKey()).toString(),
value.initCronKey());
} catch (ParseException e) {
e.printStackTrace();
} catch (SchedulerException e) {
e.printStackTrace();
}
});
}
// scheduleJob(ContentStatisticsJob.class, null, "1/1 * * * * ?",
// "exampleJob", "trigger1");
}
public Date scheduleJob(Class<? extends QuartzJobBean> object, JobDataMap jMap, String frequency, String jobName,
String triggerName) throws ParseException, SchedulerException {
JobDetailFactoryBean jobDetailFactoryBean = schedulerJobFactory.job(object, jMap, jobName);
CronTriggerFactoryBean cronTriggerFactoryBean = schedulerTriggerFactory
.jobTrigger(jobDetailFactoryBean.getObject(), frequency, triggerName);
return schedulerFactoryBean.getScheduler().scheduleJob(jobDetailFactoryBean.getObject(),
cronTriggerFactoryBean.getObject());
}
/**
* 基本校验
*
* @Description
* @param aTaskStandardJob
* @return
*/
public boolean baseCheck(AStandardBaseJob aTaskStandardJob) {
if (StringUtils.isBlank(aTaskStandardJob.initAppId())) {
LOGGER.error("【task扫描注册出现异常】-类[{}]-[appId]-不能为空", aTaskStandardJob.getClass());
return false;
}
if (StringUtils.isBlank(aTaskStandardJob.initCronKey())) {
LOGGER.error("【task扫描注册出现异常】-类[{}]-[CronKey]-不能为空", aTaskStandardJob.getClass());
return false;
}
if (StringUtils.isBlank(aTaskStandardJob.initCronValue())) {
LOGGER.error("【task扫描注册出现异常】-类[{}]-[CronValue]-不能为空", aTaskStandardJob.getClass());
return false;
}
if (aTaskStandardJob.initValidSign() == BaseGlobalConstants.VALID_SIGN_N) {
LOGGER.info("【task扫描注册】-类[{}]-[valid_sign]-为无效", aTaskStandardJob.getClass());
return false;
}
return true;
}
}
通用job:
public abstract class AStandardBaseJob extends QuartzJobBean{
/**
* 初始化appid
*
* @Description
* @return appId
*/
public abstract String initAppId();
/**
* 初始化Cron表达式key
*
* @Description
* @return cronKey
*/
public abstract String initCronKey();
/**
* 初始化Cron表达式值
*
* @Description
* @return 表达式值
*/
public abstract String initCronValue();
/**
* 初始化有效标志
*
* @Description
* @return 表达式值
*/
public abstract int initValidSign();
}
springboot 2.0时代
引入依赖
<!--quartz依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
在我们添加spring-boot-starter-quartz依赖后就不需要主动声明工厂类,因为spring-boot-starter-quartz已经为我们自动化配置好了。展开spring-boot-autoconfigure-2.0.0.RELEASE.jar,找到org.springframework.boot.autoconfigure.quartz,该目录就是SpringBoot为我们提供的Quartz自动化配置源码实现,在该目录下有如下所示几个类:
AutowireCapableBeanJobFactory 该类替代了我们之前在QuartzConfiguration配置类的AutowiringSpringBeanJobFactory内部类实现,主要作用是我们自定义的QuartzJobBean子类被Spring IOC进行托管,可以在定时任务类内使用注入任意被Spring IOC托管的类。
JobStoreType 该类是一个枚举类型,定义了对应application.yml、application.properties文件内spring.quartz.job-store-type配置,其目的是配置quartz任务的数据存储方式,分别为:MEMORY(内存方式:默认)、JDBC(数据库方式)。
QuartzAutoConfiguration 该类是自动配置的主类,内部配置了SchedulerFactoryBean以及JdbcStoreTypeConfiguration,使用QuartzProperties作为属性自动化配置条件。
QuartzDataSourceInitializer 该类主要用于数据源初始化后的一些操作,根据不同平台类型的数据库进行选择不同的数据库脚本。
QuartzProperties 该类对应了spring.quartz在application.yml、application.properties文件内开头的相关配置。
SchedulerFactoryBeanCustomizer 这是一个接口,我们实现该接口后并且将实现类使用Spring IOC托管,可以完成SchedulerFactoryBean的个性化设置,这里的设置完全可以对SchedulerFactoryBean做出全部的设置变更。
spring.quartz.properties 该配置其实代替了之前的quartz.properties,我们把之前quartz.properties配置文件内的所有配置转换成YAML风格,对应的添加在该配置下即可,在QuartzAutoConfiguration类内,会自动调用SchedulerFactoryBean的setQuartzProperties方法,把spring.quartz.properties内的所有配置进行设置。


实战
表说明
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;
CREATE TABLE QRTZ_JOB_DETAILS(
SCHED_NAME VARCHAR(120) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME VARCHAR(250) NOT NULL,
IS_DURABLE VARCHAR(1) NOT NULL,
IS_NONCONCURRENT VARCHAR(1) NOT NULL,
IS_UPDATE_DATA VARCHAR(1) NOT NULL,
REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
NEXT_FIRE_TIME BIGINT(13) NULL,
PREV_FIRE_TIME BIGINT(13) NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16) NOT NULL,
TRIGGER_TYPE VARCHAR(8) NOT NULL,
START_TIME BIGINT(13) NOT NULL,
END_TIME BIGINT(13) NULL,
CALENDAR_NAME VARCHAR(200) NULL,
MISFIRE_INSTR SMALLINT(2) NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
REPEAT_COUNT BIGINT(7) NOT NULL,
REPEAT_INTERVAL BIGINT(12) NOT NULL,
TIMES_TRIGGERED BIGINT(10) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_CRON_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
CRON_EXPRESSION VARCHAR(120) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_SIMPROP_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
STR_PROP_1 VARCHAR(512) NULL,
STR_PROP_2 VARCHAR(512) NULL,
STR_PROP_3 VARCHAR(512) NULL,
INT_PROP_1 INT NULL,
INT_PROP_2 INT NULL,
LONG_PROP_1 BIGINT NULL,
LONG_PROP_2 BIGINT NULL,
DEC_PROP_1 NUMERIC(13,4) NULL,
DEC_PROP_2 NUMERIC(13,4) NULL,
BOOL_PROP_1 VARCHAR(1) NULL,
BOOL_PROP_2 VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_BLOB_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
BLOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_CALENDARS (
SCHED_NAME VARCHAR(120) NOT NULL,
CALENDAR_NAME VARCHAR(200) NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
ENGINE=InnoDB;
CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_FIRED_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
ENTRY_ID VARCHAR(95) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
FIRED_TIME BIGINT(13) NOT NULL,
SCHED_TIME BIGINT(13) NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR(16) NOT NULL,
JOB_NAME VARCHAR(200) NULL,
JOB_GROUP VARCHAR(200) NULL,
IS_NONCONCURRENT VARCHAR(1) NULL,
REQUESTS_RECOVERY VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID))
ENGINE=InnoDB;
CREATE TABLE QRTZ_SCHEDULER_STATE (
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
CHECKIN_INTERVAL BIGINT(13) NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
ENGINE=InnoDB;
CREATE TABLE QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;
CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
commit;
说明:
QRTZ_CALENDARS 以 Blob 类型存储 Quartz 的 Calendar 信息
QRTZCRONTRIGGERS 存储 Cron Trigger,包括 Cron表达式和时区信息
QRTZFIREDTRIGGERS 存储与已触发的 Trigger 相关的状态信息,以及相联 Job的执行信息 QRTZPAUSEDTRIGGER_GRPS 存储已暂停的 Trigger 组的信息
QRTZSCHEDULERSTATE 存储少量的有关 Scheduler 的状态信息,和别的 Scheduler实例(假如是用于一个集群中)
QRTZ_LOCKS 存储程序的悲观锁的信息(假如使用了悲观锁)
QRTZJOBDETAILS 存储每一个已配置的 Job 的详细信息
QRTZJOBLISTENERS 存储有关已配置的 JobListener 的信息
QRTZSIMPLETRIGGERS 存储简单的Trigger,包括重复次数,间隔,以及已触的次数
QRTZBLOGTRIGGERS Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候)
QRTZTRIGGERLISTENERS 存储已配置的 TriggerListener 的信息
QRTZ_TRIGGERS 存储已配置的 Trigger 的信息
表字段说明:
表qrtz_job_details: 保存job详细信息,该表需要用户根据实际情况初始化
job_name:集群中job的名字,该名字用户自己可以随意定制,无强行要求
job_group:集群中job的所属组的名字,该名字用户自己随意定制,无强行要求
job_class_name:集群中个note job实现类的完全包名,quartz就是根据这个路径到classpath找到该job类
is_durable:是否持久化,把该属性设置为1,quartz会把job持久化到数据库中
job_data:一个blob字段,存放持久化job对象
表qrtz_triggers: 保存trigger信息
trigger_name: trigger的名字,该名字用户自己可以随意定制,无强行要求
trigger_group:trigger所属组的名字,该名字用户自己随意定制,无强行要求
job_name: qrtz_job_details表job_name的外键
job_group: qrtz_job_details表job_group的外键
trigger_state:当前trigger状态,设置为ACQUIRED,如果设置为WAITING,则job不会触发;暂停任务时状态为PAUSED
trigger_cron:触发器类型,使用cron表达式
表qrtz_cron_triggers:存储cron表达式表
trigger_name: qrtz_triggers表trigger_name的外键
trigger_group: qrtz_triggers表trigger_group的外键
cron_expression:cron表达式
表qrtz_scheduler_state:存储集群中note实例信息,quartz会定时读取该表的信息判断集群中每个实例的当前状态
instance_name:之前配置文件中org.quartz.scheduler.instanceId配置的名字,就会写入该字段,如果设置为AUTO,quartz会根据物理机名和当前时间产生一个名字
last_checkin_time:上次检查时间
checkin_interval:检查间隔时间
springboot 配置说明
spring.quartz.properties.org.quartz.scheduler.instanceName = clusteredScheduler
spring.quartz.properties.org.quartz.scheduler.instanceId = AUTO
spring.quartz.properties.org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
spring.quartz.properties.org.quartz.jobStore.tablePrefix = qrtz_
spring.quartz.properties.org.quartz.jobStore.isClustered = true
spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval = 10000
spring.quartz.properties.org.quartz.jobStore.useProperties = false
spring.quartz.properties.org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
spring.quartz.properties.org.quartz.threadPool.threadCount = 2
spring.quartz.properties.org.quartz.threadPool.threadPriority = 2
spring.quartz.properties.org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
spring.quartz.job-store-type = jdbc
spring.quartz.startup-delay=5s
spring.quartz.jdbc.initialize-schema=never
spring.quartz.overwrite-existing-jobs=false
执行定时任务
@Component
public class TaskRunner implements ApplicationRunner {
private final static Logger LOGGER = LoggerFactory.getLogger(TaskRunner.class);
@Autowired
private IQrtzJobDetailsService jobService;
@Autowired
private Scheduler scheduler;
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public void run(ApplicationArguments var) throws Exception{
Long count = jobService.countQuartzEntity();
if(count==0){
LOGGER.info("初始化测试任务");
QuartzEntity quartz = new QuartzEntity();
quartz.setJobName("test01");
quartz.setJobGroup("test");
quartz.setDescription("测试任务");
quartz.setJobClassName("com.ambition.common.quartz.job.TestJob");
quartz.setCronExpression("0/20 * * * * ?");
Class cls = Class.forName(quartz.getJobClassName()) ;
cls.newInstance();
//构建job信息
JobDetail job = JobBuilder.newJob(cls).withIdentity(quartz.getJobName(),
quartz.getJobGroup())
.withDescription(quartz.getDescription()).build();
// 触发时间点
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(quartz.getCronExpression());
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger"+quartz.getJobName(), quartz.getJobGroup())
.startNow().withSchedule(cronScheduleBuilder).build();
//交由Scheduler安排触发
scheduler.scheduleJob(job, trigger);
}
}
}
任务类:
/**
* 实现序列化接口、防止重启应用出现quartz Couldn't retrieve job because a required class was not found 的问题
*
* Job 的实例要到该执行它们的时候才会实例化出来。每次 Job 被执行,一个新的 Job 实例会被创建。
* 其中暗含的意思就是你的 Job 不必担心线程安全性,因为同一时刻仅有一个线程去执行给定 Job 类的实例,甚至是并发执行同一 Job 也是如此。
* @DisallowConcurrentExecution 保证上一个任务执行完后,再去执行下一个任务,这里的任务是同一个任务
*/
@DisallowConcurrentExecution
public class TestJob implements Job,Serializable {
private static final long serialVersionUID = 1L;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("任务执行成功");
}
}
controller类:
@Api(tags ="Quartz任务")
@RestController
@RequestMapping("/job")
public class JobController {
private final static Logger LOGGER = LoggerFactory.getLogger(JobController.class);
@Autowired
private Scheduler scheduler;
@Autowired
private IQrtzJobDetailsService jobService;
@SuppressWarnings({ "unchecked", "rawtypes" })
@ApiOperation(value="新建任务")
@PostMapping("/add")
public R save(QuartzEntity quartz){
LOGGER.info("新增任务");
try {
//获取Scheduler实例、废弃、使用自动注入的scheduler、否则spring的service将无法注入
//Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//如果是修改 展示旧的 任务
if(quartz.getOldJobGroup()!=null){
JobKey key = new JobKey(quartz.getOldJobName(),quartz.getOldJobGroup());
scheduler.deleteJob(key);
}
Class cls = Class.forName(quartz.getJobClassName()) ;
cls.newInstance();
//构建job信息
JobDetail job = JobBuilder.newJob(cls).withIdentity(quartz.getJobName(),
quartz.getJobGroup())
.withDescription(quartz.getDescription()).build();
// 触发时间点
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(quartz.getCronExpression());
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger"+quartz.getJobName(), quartz.getJobGroup())
.startNow().withSchedule(cronScheduleBuilder).build();
//交由Scheduler安排触发
scheduler.scheduleJob(job, trigger);
} catch (Exception e) {
LOGGER.error("",e);
return R.error();
}
return R.ok();
}
@ApiOperation(value="任务列表")
@PostMapping("/list")
public R list(QuartzEntity quartz,Integer pageNo,Integer pageSize){
LOGGER.info("任务列表");
List<QuartzEntity> list = jobService.listQuartzEntity();
return R.ok(list);
}
@ApiOperation(value="触发任务")
@PostMapping("/trigger")
public R trigger(QuartzEntity quartz, HttpServletResponse response) {
try {
JobKey key = new JobKey(quartz.getJobName(),quartz.getJobGroup());
scheduler.triggerJob(key);
} catch (SchedulerException e) {
e.printStackTrace();
return R.error();
}
return R.ok();
}
@PostMapping("/pause")
public R pause(QuartzEntity quartz, HttpServletResponse response) {
LOGGER.info("停止任务");
try {
JobKey key = new JobKey(quartz.getJobName(),quartz.getJobGroup());
scheduler.pauseJob(key);
} catch (SchedulerException e) {
e.printStackTrace();
return R.error();
}
return R.ok();
}
@PostMapping("/resume")
public R resume(QuartzEntity quartz, HttpServletResponse response) {
LOGGER.info("恢复任务");
try {
JobKey key = new JobKey(quartz.getJobName(),quartz.getJobGroup());
scheduler.resumeJob(key);
} catch (SchedulerException e) {
e.printStackTrace();
return R.error();
}
return R.ok();
}
@ApiOperation(value="移除任务")
@PostMapping("/remove")
public R remove(QuartzEntity quartz, HttpServletResponse response) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(quartz.getJobName(), quartz.getJobGroup());
// 停止触发器
scheduler.pauseTrigger(triggerKey);
// 移除触发器
scheduler.unscheduleJob(triggerKey);
// 删除任务
scheduler.deleteJob(JobKey.jobKey(quartz.getJobName(), quartz.getJobGroup()));
System.out.println("removeJob:"+ JobKey.jobKey(quartz.getJobName()));
} catch (Exception e) {
e.printStackTrace();
return R.error();
}
return R.ok();
}
}
可以通过controller提供的接口进行任务的动态添加和删除,也可以和TaskRunner那样硬编码加载任务。




