定时任务--quartz实践
定时任务--quartz实践
实际开发中简单定时任务可以使用quartz,本次是在springboot中使用。
依赖
<!--spring quartz依赖-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
数据库
quartz需要存储定时任务关键信息,例如cron表达式、触发器等
## 保存job详细信息的表
CREATE TABLE IF NOT EXISTS QRTZ_JOB_DETAILS(
SCHED_NAME VARCHAR(120) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,##job的名字
JOB_GROUP VARCHAR(200) NOT NULL,##job的所属组的名字
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME VARCHAR(250) NOT NULL,##job实现类的完全包名,quartz就是根据这个路径到classpath找到该job类
IS_DURABLE VARCHAR(1) NOT NULL,#是否持久化,把该属性设置为1,quartz会把job持久化到数据库中
IS_NONCONCURRENT VARCHAR(1) NOT NULL,
IS_UPDATE_DATA VARCHAR(1) NOT NULL,
REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
JOB_DATA BLOB NULL,#一个blob字段,存放持久化job对象
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;
#保存trigger信息 触发器信息表
CREATE TABLE IF NOT EXISTS QRTZ_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,#trigger的名字,
TRIGGER_GROUP VARCHAR(200) NOT NULL,#trigger所属组的名字
JOB_NAME VARCHAR(200) NOT NULL,#qrtz_job_details表job_name的外键
JOB_GROUP VARCHAR(200) NOT NULL,#qrtz_job_details表job_group的外键
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状态,设置为ACQUIRED,如果设置为WAITING,则job不会触发
TRIGGER_TYPE VARCHAR(8) NOT NULL,#CRON
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;
#存储cron表达式表
CREATE TABLE IF NOT EXISTS QRTZ_CRON_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,#triggers表trigger_name的外键
TRIGGER_GROUP VARCHAR(200) NOT NULL,# qrtz_triggers表trigger_group的外键
CRON_EXPRESSION VARCHAR(120) NOT NULL,#cron表达式
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 IF NOT EXISTS 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 IF NOT EXISTS 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 IF NOT EXISTS 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 IF NOT EXISTS 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 IF NOT EXISTS 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 IF NOT EXISTS 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 IF NOT EXISTS 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 IF NOT EXISTS QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;
application配置
关于配置详细解释:https://blog.csdn.net/zixiao217/article/details/53091812
#主要分为scheduler、threadPool、jobStore、dataSource等部分
org.quartz.scheduler.instanceId=AUTO
org.quartz.scheduler.instanceName=DefaultQuartzScheduler
#如果您希望Quartz Scheduler通过RMI作为服务器导出本身,则将“rmi.export”标志设置为true
#在同一个配置文件中为'org.quartz.scheduler.rmi.export'和'org.quartz.scheduler.rmi.proxy'指定一个'true'值是没有意义的,如果你这样做'export'选项将被忽略
org.quartz.scheduler.rmi.export=false
#如果要连接(使用)远程服务的调度程序,则将“org.quartz.scheduler.rmi.proxy”标志设置为true。您还必须指定RMI注册表进程的主机和端口 - 通常是“localhost”端口1099
org.quartz.scheduler.rmi.proxy=false
org.quartz.scheduler.wrapJobExecutionInUserTransaction=false
#实例化ThreadPool时,使用的线程类为SimpleThreadPool
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
#threadCount和threadPriority将以setter的形式注入ThreadPool实例
#并发个数 如果你只有几个工作每天触发几次 那么1个线程就可以,如果你有成千上万的工作,每分钟都有很多工作 那么久需要50-100之间.
#只有1到100之间的数字是非常实用的
org.quartz.threadPool.threadCount=5
#优先级 默认值为5
org.quartz.threadPool.threadPriority=5
#可以是“true”或“false”,默认为false
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
#在被认为“misfired”(失火)之前,调度程序将“tolerate(容忍)”一个Triggers(触发器)将其下一个启动时间通过的毫秒数。默认值(如果您在配置中未输入此属性)为60000(60秒)
org.quartz.jobStore.misfireThreshold=5000
# 默认存储在内存中,RAMJobStore快速轻便,但是当进程终止时,所有调度信息都会丢失
#org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore
#持久化方式,默认存储在内存中,此处使用数据库方式
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
#您需要为JobStore选择一个DriverDelegate才能使用。DriverDelegate负责执行特定数据库可能需要的任何JDBC工作
# StdJDBCDelegate是一个使用“vanilla”JDBC代码(和SQL语句)来执行其工作的委托,用于完全符合JDBC的驱动程序
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#可以将“org.quartz.jobStore.useProperties”配置参数设置为“true”(默认为false),以指示JDBCJobStore将JobDataMaps中的所有值都作为字符串,
#因此可以作为名称 - 值对存储而不是在BLOB列中以其序列化形式存储更多复杂的对象。从长远来看,这是更安全的,因为您避免了将非String类序列化为BLOB的类版本问题
org.quartz.jobStore.useProperties=true
#表前缀
org.quartz.jobStore.tablePrefix=QRTZ_
#数据源别名,自定义
org.quartz.jobStore.dataSource=qzDS
#使用阿里的druid作为数据库连接池
org.quartz.dataSource.qzDS.connectionProvider.class=org.example.config.DruidPoolingconnectionProvider
org.quartz.dataSource.qzDS.URL=jdbc:mysql://127.0.0.1:3306/test_quartz?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTC
org.quartz.dataSource.qzDS.user=root
org.quartz.dataSource.qzDS.password=123456
org.quartz.dataSource.qzDS.driver=com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.maxConnections=10
#设置为“true”以打开群集功能。如果您有多个Quartz实例使用同一组数据库表,则此属性必须设置为“true”,否则您将遇到破坏
#org.quartz.jobStore.isClustered=false
配置类
配置是否开始定时任务
quartz:
status: false
@Component
//有些时候不需要时刻开启定时任务
@ConditionalOnProperty(value = "quartz.status")
public class PpeJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
项目也要监控定时任务配置
数据库,可以根据自己情况自定义。
DROP TABLE IF EXISTS `cron_metadata`;
CREATE TABLE `cron_metadata` (
`cron_metadata_id` bigint NOT NULL,-- 主键
`cron` varchar(255) CHARACTER NULL DEFAULT NULL,-- cron表达式
`cron_group` varchar(255) CHARACTER NULL DEFAULT NULL, -- 分组
`cron_code` varchar(255) CHARACTER NULL DEFAULT NULL,-- code
`cron_name` varchar(255) CHARACTER NULL DEFAULT NULL,-- 名称
`start_time` datetime NULL DEFAULT NULL,-- 开始时间
`end_time` datetime NULL DEFAULT NULL,-- 结束时间
`actived` tinyint NULL DEFAULT 0,-- 是否开启
`status` tinyint NULL DEFAULT 0,-- 状态
`params` varchar(255) CHARACTER NULL DEFAULT NULL,-- 描述
`url` varchar(255) CHARACTER NULL DEFAULT NULL,
`sys_default_flag` tinyint NULL DEFAULT 0,
`seq_num` int NULL DEFAULT 0,
`reg_hum_guid` varchar(36) CHARACTER NULL DEFAULT NULL,
`reg_hum_name` varchar(100) CHARACTER NULL DEFAULT NULL,
`reg_date` datetime NULL DEFAULT NULL,
`upd_hum_guid` varchar(36) CHARACTER NULL DEFAULT NULL,
`upd_hum_name` varchar(100) CHARACTER NULL DEFAULT NULL,
`upd_date` datetime NULL DEFAULT NULL,
`memo` varchar(4000) CHARACTER NULL DEFAULT NULL,
`tenant_id` varchar(36) CHARACTER NULL DEFAULT NULL,
PRIMARY KEY (`cron_metadata_id`) USING BTREE
);
工具类
/**
* 定时任务的工具类
*/
public class TaskUtils
{
/**
* 产生JobKey
*
* @param job
* @return
*/
public static JobKey genCronJobKey(CronMetadata job)
{
return new JobKey(job.getCronCode().trim(),job.getCronGroup().trim());
}
/**
* 产生TriggerKey
*
* @param job
* @return
*/
public static TriggerKey genCronTriggerKey(CronMetadata job)
{
return new TriggerKey(job.getCronCode().trim(),job.getCronGroup().trim());
}
/**
* 判断是否两个trigger key是否相等
*
* @param tk1
* @param tk2
* @return
*/
public static boolean isTriggerKeyEqual(TriggerKey tk1, TriggerKey tk2)
{
return tk1.getName().equals(tk2.getName()) && ((tk1.getGroup() == null && tk2.getGroup() == null)
|| (tk1.getGroup() != null && tk1.getGroup().equals(tk2.getGroup())));
}
}
启动和初始化配置
InitService类
@Component
@Slf4j
@ConditionalOnProperty(value = "quartz.status")
public class InitService {
@Autowired
private ScheduleService scheduleService;
@Autowired
private SchedulerFactoryBean schedulerFactoryBean;
/**
* 初始化
*/
//@PostConstruct
public void init() {
// 初始化基于cron时间配置的任务列表
try {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
scheduler.shutdown();
scheduleService.initCronJobs();
} catch (Exception e) {
log.error("init cron tasks error," + e.getMessage(), e);
// throw new RuntimeException("初始化定时任务失败!");
}
}
}
定时任务初始化服务类
@Component
@Slf4j
public class ScheduleService {
@Autowired
private QrtzTriggersDao qrtzTriggersDao;
@Resource
private CronMetadataDao cronMetadataDao;
@Autowired
private SchedulerFactoryBean schedulerFactoryBean;
/**
* 初始化任务(基于cron触发器)
*/
public void initCronJobs() {
syncTriggers();
//查询启动状态值为true(1)的启动
Iterable<CronMetadata> jobList = cronMetadataDao.selectList(
new LambdaQueryWrapper<CronMetadata>().eq(CronMetadata::getActived, true));
for (CronMetadata job : jobList) {
scheduleCronJob(job);
}
}
/***
* 同步定时任务 保证任务列表和业务表一致
* @throws SchedulerException
*/
public void syncTriggers() {
//未与业务表同步的触发器
List<QrtzTriggers> triggers = qrtzTriggersDao.selectTriggers();
TriggerKey triggerKey;
for (QrtzTriggers qrtzTriggers : triggers) {
//删除所有业务表中不存在的任务及触发器
triggerKey = new TriggerKey(qrtzTriggers.getTriggerName(), qrtzTriggers.getTriggerGroup());
try {
schedulerFactoryBean.getScheduler().pauseTrigger(triggerKey);
schedulerFactoryBean.getScheduler().unscheduleJob(triggerKey);
schedulerFactoryBean.getScheduler().deleteJob(new JobKey(qrtzTriggers.getTriggerName(), qrtzTriggers.getTriggerGroup()));
} catch (SchedulerException e) {
log.error("同步定时任务异常:" + e.getMessage(), e);
throw new RuntimeException("同步定时任务异常!");
}
}
}
/**
* 安排任务(基于cron触发器)
*
* @param job job
*/
public void scheduleCronJob(CronMetadata job) {
if (job != null && StringUtils.isNotBlank(job.getCronCode()) && StringUtils.isNotBlank(job.getUrl())
&& StringUtils.isNotBlank(job.getCron())) {
//判断状态无需启动则返回
if (!job.getStatus()) {
return;
}
JobKey jobKey = TaskUtils.genCronJobKey(job);
try {
if (!schedulerFactoryBean.getScheduler().isStarted()) {
schedulerFactoryBean.start();
}
if (!schedulerFactoryBean.getScheduler().checkExists(jobKey)) {
log.info("Add new cron job to scheduler, jobName = " + job.getCronCode());
this.newJobAndNewCronTrigger(job);
} else {
log.info("Update cron job to scheduler, jobName = " + job.getCronCode());
this.updateCronTriggerOfJob(job);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException("当前传入url不合法或类不存在!");
} catch (SchedulerException e) {
throw new RuntimeException(e.getMessage());
}
} else {
log.error("Method scheduleCronJob arguments are invalid.");
throw new RuntimeException("当前传入参数为空或不合法!");
}
}
/**
* 新建job和trigger到scheduler(基于cron触发器)
*
* @param job
* @throws SchedulerException
* @throws ClassNotFoundException
*/
public void newJobAndNewCronTrigger(CronMetadata job)
throws SchedulerException, ClassNotFoundException {
JobKey jobKey = TaskUtils.genCronJobKey(job);
TriggerKey triggerKey = TaskUtils.genCronTriggerKey(job);
String cronExpr = job.getCron();
if (!isValidExpression(cronExpr)) {
log.error("传入定时任务cron表达式不合法" + cronExpr);
throw new ServiceException("传入定时任务cron表达式不合法!");
}
// get a Class object by string class name of job;
String url = Constants.DEFAULT_PACKAGE + job.getUrl().trim();
Class jobClass = Class.forName(url);
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobKey).withDescription(job.getCronName())
.build();
TriggerBuilder trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).forJob(jobKey)
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpr).withMisfireHandlingInstructionDoNothing());
if (job.getStartTime() != null) {
trigger.startAt(job.getStartTime());
}
if (job.getEndTime() != null) {
trigger.endAt(job.getEndTime());
}
Trigger t = trigger.build();
t.getJobDataMap().put("cronMetadata", job);
schedulerFactoryBean.getScheduler().scheduleJob(jobDetail, t);
}
/**
* 更新job的trigger(基于cron触发器)
*
* @param job
* @throws SchedulerException
*/
public void updateCronTriggerOfJob(CronMetadata job) throws SchedulerException {
JobKey jobKey = TaskUtils.genCronJobKey(job);
TriggerKey triggerKey = TaskUtils.genCronTriggerKey(job);
String cronExpr = job.getCron().trim();
List<? extends Trigger> triggers = schedulerFactoryBean.getScheduler().getTriggersOfJob(jobKey);
for (int i = 0; triggers != null && i < triggers.size(); i++) {
Trigger trigger = triggers.get(i);
TriggerKey curTriggerKey = trigger.getKey();
if (isValidExpression(job.getCron())) {
TriggerBuilder newTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).forJob(jobKey)
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpr)
.withMisfireHandlingInstructionDoNothing());
if (job.getStartTime() != null) {
newTrigger.startAt(job.getStartTime());
}
if (job.getEndTime() != null) {
newTrigger.endAt(job.getEndTime());
}
Trigger t = newTrigger.build();
t.getJobDataMap().put("cronMetadata", job);
schedulerFactoryBean.getScheduler().rescheduleJob(curTriggerKey, t);
} else {
throw new ServiceException("当前定时任务cron[" + job.getCron() + "]表达式不合法!");
}
//如果是修改需求启动任务
if (job.getStatus()) {
this.resumeOne(job);
}
// }
// } else {
// scheduler.unscheduleJob(curTriggerKey);
// }
}
}
/***
* 删除指定job
* @param cronMetadata job
* @throws SchedulerException
*/
public void removeJob(CronMetadata cronMetadata) {
JobKey jobKey = TaskUtils.genCronJobKey(cronMetadata);
TriggerKey triggerKey = TaskUtils.genCronTriggerKey(cronMetadata);
try {
schedulerFactoryBean.getScheduler().pauseTrigger(triggerKey);
schedulerFactoryBean.getScheduler().unscheduleJob(triggerKey);
schedulerFactoryBean.getScheduler().deleteJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
throw new ServiceException(500, "关闭定时任务失败!");
}
}
/**
* 暂停指定 job
*
* @param cronMetadata
* @throws SchedulerException
*/
public void pauseJob(CronMetadata cronMetadata) {
JobKey jobKey = TaskUtils.genCronJobKey(cronMetadata);
TriggerKey triggerKey = TaskUtils.genCronTriggerKey(cronMetadata);
try {
schedulerFactoryBean.getScheduler().pauseTrigger(triggerKey);
schedulerFactoryBean.getScheduler().pauseJob(jobKey);
} catch (SchedulerException e) {
e.printStackTrace();
throw new ServiceException(500, "暂停定时任务失败!");
}
}
/**
* 关闭所有job
*
* @throws SchedulerException
*/
public void pauseAll() throws SchedulerException {
schedulerFactoryBean.getScheduler().pauseAll();
}
/**
* 暂停后启动指定Job
*/
public void resumeOne(CronMetadata cronMetadata) throws SchedulerException {
JobKey jobKey = TaskUtils.genCronJobKey(cronMetadata);
TriggerKey triggerKey = TaskUtils.genCronTriggerKey(cronMetadata);
schedulerFactoryBean.getScheduler().resumeJob(jobKey);
schedulerFactoryBean.getScheduler().resumeTrigger(triggerKey);
}
/**
* 启动所有Job
*/
public void resumeAll(List<CronMetadata> cronMetadatas) throws SchedulerException {
for (CronMetadata job : cronMetadatas) {
scheduleCronJob(job);
}
}
/**
* 暂停后启动所以Job
*/
public void test() throws SchedulerException {
schedulerFactoryBean.getScheduler().getTriggerGroupNames();
}
}
创建任务类
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
@Component
public class HelloJob extends QuartzJobBean {
static long a = 0;
@Autowired
private CronInstanceService cronInstanceService;
/***
* job执行方法
* @param jobExecutionContext
*/
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) {
a++;
CronMetadata cronMetadata = (CronMetadata)jobExecutionContext.getMergedJobDataMap().get("cronMetadata");
CronInstance cronInstance = new CronInstance();
cronInstance.setCronMetadataId(cronMetadata.getCronMetadataId()).setExecTime(new Date());
try {
if(a%5!=0){
System.out.println(jobExecutionContext.getTrigger().getKey()+"我执行了!"+a);
cronInstance.setSuccessFlag(true);
}else {
throw new RuntimeException("我报错了!");
}
}catch (Exception e){
cronInstance.setSuccessFlag(false)
.setDetail(e.getMessage());
}
finally {
cronInstanceService.save(cronInstance);
}
}
}
对于项目观察和配置的接口
Dao和mapper
@Mapper
public interface CronMetadataDao extends BaseMapper<CronMetadata> {
CronMetadata selectOneByCronGroupAndCronCode(@Param("cronGroup") String cronGroup, @Param("cronCode") String cronCode);
List<CronMetadata> selectAll();
int addAll(CronMetadata cronMetadata);
int delByCronGroupAndCronCode(@Param("cronGroup") String cronGroup, @Param("cronCode") String cronCode);
int countByCronGroupAndCronCode(@Param("cronGroup") String cronGroup, @Param("cronCode") String cronCode);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.puhua.server.plan.boot.mapper.CronMetadataDao">
<resultMap id="BaseResultMap" type="com.puhua.server.plan.boot.entity.CronMetadata">
<id property="cronMetadataId" column="cron_metadata_id" jdbcType="BIGINT"/>
<result property="cron" column="cron" jdbcType="VARCHAR"/>
<result property="cronGroup" column="cron_group" jdbcType="VARCHAR"/>
<result property="cronName" column="cron_name" jdbcType="VARCHAR"/>
<result property="cronCode" column="cron_code" jdbcType="VARCHAR"/>
<result property="startTime" column="start_time" jdbcType="TIMESTAMP"/>
<result property="endTime" column="end_time" jdbcType="TIMESTAMP"/>
<result property="status" column="status" jdbcType="TINYINT"/>
<result property="actived" column="actived" jdbcType="TINYINT"/>
<result property="params" column="params" jdbcType="VARCHAR"/>
<result property="url" column="url" jdbcType="VARCHAR"/>
<result property="seqNum" column="seq_num" jdbcType="INTEGER"/>
<result property="regHumGuid" column="reg_hum_guid" jdbcType="VARCHAR"/>
<result property="regHumName" column="reg_hum_name" jdbcType="VARCHAR"/>
<result property="regDate" column="reg_date" jdbcType="TIMESTAMP"/>
<result property="updHumGuid" column="upd_hum_guid" jdbcType="VARCHAR"/>
<result property="updHumName" column="upd_hum_name" jdbcType="VARCHAR"/>
<result property="updDate" column="upd_date" jdbcType="TIMESTAMP"/>
<result property="memo" column="memo" jdbcType="VARCHAR"/>
<result property="tenantId" column="tenant_id" jdbcType="VARCHAR"/>
</resultMap>
<sql id="Base_Column_List">
cron_metadata_id
,cron,cron_group,
cron_code,cron_name,start_time,end_time,
status,actived,params,url,
seq_num,reg_hum_guid,
reg_hum_name,reg_date,upd_hum_guid,
upd_hum_name,upd_date,memo
</sql>
<select id="selectOneByCronGroupAndCronCode" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from cron_metadata
where
cron_group = #{cronGroup,jdbcType=VARCHAR}
AND cron_code = #{cronCode,jdbcType=VARCHAR}
</select>
<select id="selectAll" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from cron_metadata
</select>
<insert id="addAll">
insert into cron_metadata
(cron_metadata_id, cron, cron_group,
cron_code, cron_name, start_time, end_time,
status, actived, params, url,
seq_num, reg_hum_guid,
reg_hum_name, reg_date, upd_hum_guid,
upd_hum_name, upd_date, memo,tenant_id)
values (#{cronMetadataId,jdbcType=NUMERIC}, #{cron,jdbcType=VARCHAR}, #{cronGroup,jdbcType=VARCHAR},
#{cronCode,jdbcType=VARCHAR}, #{cronName,jdbcType=VARCHAR}, #{startTime,jdbcType=TIMESTAMP},
#{endTime,jdbcType=TIMESTAMP},
#{status,jdbcType=NUMERIC}, #{params,jdbcType=NUMERIC}, #{params,jdbcType=VARCHAR},
#{url,jdbcType=VARCHAR},
#{seqNum,jdbcType=NUMERIC}, #{regHumGuid,jdbcType=VARCHAR},
#{regHumName,jdbcType=VARCHAR}, #{regDate,jdbcType=TIMESTAMP}, #{updHumGuid,jdbcType=VARCHAR},
#{updHumName,jdbcType=VARCHAR}, #{updDate,jdbcType=TIMESTAMP}, #{memo,jdbcType=VARCHAR},
#{tenantId,jdbcType=VARCHAR})
</insert>
<delete id="delByCronGroupAndCronCode">
delete
from cron_metadata
where cron_group = #{cronGroup,jdbcType=VARCHAR}
AND cron_code = #{cronCode,jdbcType=VARCHAR}
</delete>
<select id="countByCronGroupAndCronCode" resultType="int">
select count(*)
from cron_metadata
where cron_group = #{cronGroup,jdbcType=VARCHAR}
AND cron_code = #{cronCode,jdbcType=VARCHAR}
</select>
<update id="updateAll">
update cron_metadata
set
</update>
</mapper>
service和serviceImpl
/**
*公共Job处理业务
*/
public interface CronMetadataService extends IService<CronMetadata> {
/**
*
* @param cronMetadata 新增或者修改任务
* @return 状态
*/
void addOrUpdateJob(CronMetadata cronMetadata);
void removeJob(Long id);
/***
* 暂停任务
* @param id
*/
void pauseJob(Long id);
/***
* 启动任务
* @param id
*/
void resumeOne(Long id);
/***
* 暂停所有任务
*/
void pauseAll();
/***
* 启动所有任务
*/
void resumeAll();
/***
* 查询所有
* @param
* @return
*/
IPage<CronMetadata> queryPage(Page page, QueryWrapper queryWrapper);
/***
* 查询所有
* @param
* @return
*/
List<CronMetadata> queryList(QueryWrapper queryWrapper);
}
@Service
public class CronMetadataServiceImpl extends ServiceImpl<CronMetadataDao, CronMetadata> implements CronMetadataService {
@Autowired
private CronMetadataDao cronMetadataDao;
@Autowired
private ScheduleService scheduleService;
@Autowired
private List<QuartzJobBean> quartzJobBeans;
@Override
@Transactional
public void addOrUpdateJob(CronMetadata cronMetadata) {
if (!isValidExpression(cronMetadata.getCron())) {
throw new ServiceException("当前定时任务cron[" + cronMetadata.getCron() + "]表达式不合法!");
}
List<String> jobClassList = quartzJobBeans.stream().map(quartzJobBean -> quartzJobBean.getClass().getSimpleName()).collect(Collectors.toList());
if (!jobClassList.contains(cronMetadata.getUrl())) {
throw new ServiceException("当前传入任务类不存在或不合法!");
}
checkDate(cronMetadata.getStartTime(), cronMetadata.getEndTime());
if (cronMetadata.getCronMetadataId() != null) {
CronMetadata cronMetadataDb = cronMetadataDao.selectOne(
new LambdaQueryWrapper<CronMetadata>().eq(CronMetadata::getCronMetadataId, cronMetadata.getCronMetadataId()));
if (cronMetadataDb == null) {
throw new ServiceException(500, "当前修改任务数据库不存在,请检查后重试!");
}
if (!cronMetadata.getActived()) {
cronMetadata.setStatus(false);
}
cronMetadata.setUpdDate(new Date());
if (cronMetadata.getStatus() == null) {
cronMetadata.setStatus(cronMetadataDb.getStatus());
}
if (cronMetadata.getActived() == null) {
cronMetadata.setActived(cronMetadataDb.getActived());
}
cronMetadataDao.updateById(cronMetadata);
} else {
int count = cronMetadataDao.countByCronGroupAndCronCode(cronMetadata.getCronGroup(), cronMetadata.getCronCode());
if (count > 0) {
throw new ServiceException(500, "已有相同Job无需新增");
}
if (cronMetadata.getStatus() == null) {
cronMetadata.setStatus(false);
}
if (cronMetadata.getActived() == null) {
cronMetadata.setActived(false);
}
cronMetadata.setRegDate(new Date());
cronMetadata.setTenantId(0+"");
cronMetadataDao.insert(cronMetadata);
}
//判断状态是否启动该job
if (cronMetadata.getStatus() && cronMetadata.getActived()) {
scheduleService.scheduleCronJob(cronMetadata);
} else if (!cronMetadata.getStatus() && cronMetadata.getActived()) {
//暂停该job
scheduleService.pauseJob(cronMetadata);
} else {
scheduleService.removeJob(cronMetadata);
}
}
private void checkDate(Date startDate, Date endDate) {
if (startDate != null && endDate != null && startDate.after(endDate)) {
throw new ServiceException("开始日期不能大于结束日期!");
} else if (startDate == null && endDate != null ) {
throw new ServiceException("填写结束日期时必须填写开始日期!");
}
}
@Override
@Transactional
public void removeJob(Long id) {
CronMetadata cronMetadata = cronMetadataDao.selectOne(
new LambdaQueryWrapper<CronMetadata>().eq(CronMetadata::getCronMetadataId, id));
if (cronMetadata == null) {
scheduleService.syncTriggers();
throw new ServiceException("未查询到该任务,无法暂停!");
}
//先关闭scheduler中的当前Job
scheduleService.removeJob(cronMetadata);
//删除表中数据
cronMetadataDao.delByCronGroupAndCronCode(cronMetadata.getCronGroup(), cronMetadata.getCronCode());
}
@Override
public void pauseJob(Long id) {
CronMetadata cronMetadata = cronMetadataDao.selectOne(
new LambdaQueryWrapper<CronMetadata>().eq(CronMetadata::getCronMetadataId, id));
if (cronMetadata == null) {
scheduleService.syncTriggers();
throw new RuntimeException("未查询到该任务,无法暂停!");
} else if (!cronMetadata.getStatus()) {
throw new RuntimeException("请不要重复暂停!");
}
scheduleService.pauseJob(cronMetadata);
cronMetadata.setStatus(false);
cronMetadataDao.updateById(cronMetadata);
}
@Override
public void resumeOne(Long id) {
CronMetadata cronMetadata = cronMetadataDao.selectOne(
new LambdaQueryWrapper<CronMetadata>().eq(CronMetadata::getCronMetadataId, id));
if (cronMetadata == null) {
throw new ServiceException("未查询到该任务,无法启动!");
}
if (!cronMetadata.getActived()) {
throw new ServiceException("当前任务未生效无法启动!");
}
try {
scheduleService.resumeOne(cronMetadata);
} catch (Exception e) {
throw new ServiceException("启动job失败:" + e.getMessage());
}
cronMetadata.setStatus(true);
cronMetadataDao.updateById(cronMetadata);
}
@Override
public void pauseAll() {
List<CronMetadata> cronMetadatas = cronMetadataDao.selectList(
new LambdaQueryWrapper<CronMetadata>().eq(CronMetadata::getActived, true));
if (cronMetadatas.size() == 0) {
scheduleService.syncTriggers();
throw new ServiceException("当前没有需要暂停的任务!");
}
try {
scheduleService.pauseAll();
} catch (SchedulerException e) {
e.printStackTrace();
}
cronMetadatas = cronMetadatas.stream().map(c -> c.setStatus(false)).collect(Collectors.toList());
this.updateBatchById(cronMetadatas);
}
@Override
public void resumeAll() {
List<CronMetadata> cronMetadatas = cronMetadataDao.selectList(
new LambdaQueryWrapper<CronMetadata>().eq(CronMetadata::getActived, true));
if (cronMetadatas.size() == 0) {
throw new ServiceException("当前没有需要启动的任务!");
}
cronMetadatas = cronMetadatas.stream().map(c -> c.setStatus(true)).collect(Collectors.toList());
try {
scheduleService.resumeAll(cronMetadatas);
} catch (SchedulerException e) {
e.printStackTrace();
}
this.updateBatchById(cronMetadatas);
}
@Override
public IPage<CronMetadata> queryPage(Page page, QueryWrapper queryWrapper) {
return this.page(page, queryWrapper);
}
@Override
public List<CronMetadata> queryList(QueryWrapper queryWrapper) {
return cronMetadataDao.selectList(queryWrapper);
}
}
接口controller
@RestController
@RequestMapping("/quartz")
@Api(value="定时任务",tags = {"定时任务管理"})
public class CronMetadataController {
@Autowired
private CronMetadataService cronMetadataService;
@Autowired
private List<QuartzJobBean> quartzJobBeans;
/**
* 分页查询
*/
@ApiOperation(value = "分页查询")
@GetMapping
public Result page(Page page, @PlanQueryWrapper(CronMetadata.class) QueryWrapper query){
IPage<CronMetadata> pageList = cronMetadataService.queryPage(page,query);
return Result.success(new PageUtils(pageList));
}
/**
* 查询所有
*/
@ApiOperation(value = "查询所有")
@GetMapping("/list")
public Result list(@PlanQueryWrapper(CronMetadata.class) QueryWrapper query){
List list = cronMetadataService.queryList(query);
return Result.success(list);
}
@ApiOperation(value = "新增或者修改任务")
@PostMapping
public Result addOrUpdateJob(@RequestBody CronMetadata cronMetadata){
cronMetadataService.addOrUpdateJob(cronMetadata);
return Result.success();
}
@ApiOperation(value = "查询所有定时任务类")
@GetMapping("/findQuartzClass")
public Result findQuartzClass(){
return Result.success(quartzJobBeans.stream().map(quartzJobBean -> quartzJobBean.getClass().getSimpleName()).collect(Collectors.toList()));
}
/***
* 根据ID删除任务
* @param id
* @return
*/
@ApiOperation(value = "根据ID删除任务")
@DeleteMapping("{id}")
public Result delete(@PathVariable Long id) {
cronMetadataService.removeJob(id);
return Result.success();
}
/***
* 根据ID暂停任务
* @param id
* @return
*/
@ApiOperation(value = "根据ID暂停任务")
@GetMapping("/pause/{id}")
public Result pause(@PathVariable Long id) {
cronMetadataService.pauseJob(id);
return Result.success();
}
/***
* 根据ID启动任务
* @param id
* @return
*/
@ApiOperation(value = "根据ID启动任务")
@GetMapping("/resume/{id}")
public Result resumeOne(@PathVariable Long id) {
cronMetadataService.resumeOne(id);
return Result.success();
}
/***
* 暂停所有任务
* @return
*/
@ApiOperation(value = "暂停所有任务")
@GetMapping("/pauseAll")
public Result pauseAll() {
cronMetadataService.pauseAll();
return Result.success();
}
/***
* 启动所有任务
* @return
*/
@ApiOperation(value = "启动所有任务")
@GetMapping("/resumeAll")
public Result resumeAll() {
cronMetadataService.resumeAll();
return Result.success();
}

浙公网安备 33010602011771号