一、Quartz简介
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目。
官网:http://www.quartz-scheduler.org/
Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。
Quartz 可以与 J2EE 与 J2SE 应用程序相结合也可以单独使用。
Quartz 允许程序开发人员根据时间的间隔来调度作业。
Quartz 实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联。
Quartz 核心概念:
- Job 表示一个工作,要执行的具体内容。此接口中只有一个方法,如下:
void execute(JobExecutionContext context)
- JobDetail 表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。
- Trigger 代表一个调度参数的配置,什么时候去调。
- Scheduler 代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调度了。
二、Maven依赖
<!--quartz依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
三、Quartz表
序号 | 表名 | 说明 |
---|---|---|
1. | qrtz_calendars | 以 Blob 类型存储 Quartz 的 Calendar 信息 |
2. | qrtz_cron_triggers | 存储 Cron Trigger,包括 Cron 表达式和时区信息 |
3. | qrtz_fired_triggers | 存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息 |
4. | qrtz_paused_trigger_grps | 存储已暂停的 Trigger 组的信息 |
5. | qrtz_scheduler_state | 存储少量的有关调度器 (Scheduler) 的状态,和别的 调度器 (Scheduler)实例(假如是用于一个集群中) |
6. | qrtz_locks | 存储程序的非观锁的信息(假如使用了悲观锁) |
7. | qrtz_job_details | 存储每一个已配置的 Job 的详细信息(jobDetail) |
8. | qrtz_job_listeners | 存储有关已配置的 Job 监听器 的信息 |
9. | qrtz_simple_triggers | 存储简单的 Trigger,包括重复次数,间隔,以及已触的次数 |
10. | qrtz_blog_triggers | 以 Blob 类型存储的Trigger(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候) |
11. | qrtz_trigger_listeners | 存储已配置的触发器监听器 ( Trigger Listener ) 的信息 |
12. | qrtz_triggers | 存储已配置的 触发器 (Trigger) 的信息 |
脚本:
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;
#-- 存储每一个已配置的 Job 的详细信息
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 DEFAULT CHARSET=utf8 ;
#--存储已配置的 Trigger 的信息
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 DEFAULT CHARSET=utf8 ;
#-- 存储简单的 Trigger,包括重复次数,间隔,以及已触的次数
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 DEFAULT CHARSET=utf8 ;
#-- 存储 Cron Trigger,包括 Cron 表达式和时区信息
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 DEFAULT CHARSET=utf8 ;
#-- 存储简单的 Trigger,包括重复次数,间隔,以及已触的次数
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 DEFAULT CHARSET=utf8 ;
#-- Trigger 作为 Blob 类型存储
#-- (用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型,JobStore并不知道如何存储实例的时候)
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 DEFAULT CHARSET=utf8 ;
#-- 以 Blob 类型存储 Quartz 的 Calendar 信息
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 DEFAULT CHARSET=utf8 ;
#-- 存储已暂停的 Trigger 组的信息
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 DEFAULT CHARSET=utf8 ;
#-- 存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息
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 DEFAULT CHARSET=utf8 ;
#-- 存储少量的有关 Scheduler 的状态信息,和别的 Scheduler 实例(假如是用于一个集群中)
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 DEFAULT CHARSET=utf8 ;
#-- 存储程序的悲观锁的信息(假如使用了悲观锁)
CREATE TABLE qrtz_locks (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
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;
四、Quartz配置
# 配置集群时,quartz调度器的id,由于配置集群时,只有一个调度器,必须保证每个服务器该值都相同,只要每个都一样就行
spring.quartz.properties.org.quartz.scheduler.instanceName=quartzScheduler
# 集群中每台服务器自己的id,AUTO表示自动生成,ID设置为自动获取 每一个必须不同 (所有调度器实例中是唯一的)
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO
# 实现集群时,任务的存储实现方式,org.quartz.impl.jdbcjobstore.JobStoreTX表示数据库存储
spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
# Driver代表了解不同数据库系统的特定“方言”
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# quartz存储任务相关数据的表的前缀
spring.quartz.properties.org.quartz.jobStore.tablePrefix=QRTZ_
# 是否启用集群,启用,改为true,注意:启用集群后,必须配置下面的数据源,否则quartz调度器会初始化失败
spring.quartz.properties.org.quartz.jobStore.isClustered=true
# 集群中服务器相互检测间隔,每台服务器都会按照下面配置的时间间隔往服务器中更新自己的状态,
# 如果某台服务器超过以下时间没有checkin,调度器就会认为该台服务器已经down掉,不会再分配任务给该台服务器
spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval=10000
# 以指示JDBCJobStore将JobDataMaps中的所有值都作为字符串,因此可以作为名称 - 值对存储
# 而不是在BLOB列中以其序列化形式存储更多复杂的对象。
# 从长远来看,这是更安全的,因为您避免了将非String类序列化为BLOB的类版本问题
spring.quartz.properties.org.quartz.jobStore.useProperties=false
# 线程池
spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
# quartz线程池中线程数,可根据任务数量和负责度来调整
spring.quartz.properties.org.quartz.threadPool.threadCount=10
# quartz线程优先级
spring.quartz.properties.org.quartz.threadPool.threadPriority=5
# 线程池
spring.quartz.properties.org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
# 设置quartz任务的数据持久化方式,默认是内存方式
spring.quartz.job-store-type=JDBC
# 序启动会自动创建quartz相关表 初始化表结构ALWAYS 不初始化表结构NEVER
spring.quartz.jdbc.initialize-schema=ALWAYS
附:
org.quartz.jobStore.driverDelegateClass
Driver代表了解不同数据库系统的特定“方言”。可能的选择包括:
org.quartz.impl.jdbcjobstore.StdJDBCDelegate(用于完全符合JDBC的驱动程序)
org.quartz.impl.jdbcjobstore.MSSQLDelegate(对于Microsoft SQL Server和Sybase)
org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.impl.jdbcjobstore.WebLogicDelegate(对于WebLogic驱动程序)
org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.impl.jdbcjobstore.oracle.WebLogicOracleDelegate(对于Weblogic中使用的Oracle驱动程序)
org.quartz.impl.jdbcjobstore.oracle.weblogic.WebLogicOracleDelegate(对于在Weblogic中使用的Oracle驱动程序)
org.quartz.impl.jdbcjobstore.CloudscapeDelegate
org.quartz.impl.jdbcjobstore.DB2v6Delegate
org.quartz.impl.jdbcjobstore.DB2v7Delegate
org.quartz.impl.jdbcjobstore.DB2v8Delegate
org.quartz.impl.jdbcjobstore.HSQLDBDelegate
org.quartz.impl.jdbcjobstore.PointbaseDelegate
org.quartz.impl.jdbcjobstore.SybaseDelegate
五、Quartz 的触发时间的配置
cron 方式:采用cronExpression表达式配置时间。
simple 方式:和JavaTimer差不多,可以指定一个开始时间和结束时间外加一个循环时间。
calendars 方式:可以和cron配合使用,用cron表达式指定一个触发时间规律,用calendar指定一个范围。
注意:cron方式需要用到的4张数据表:
qrtz_triggers,qrtz_cron_triggers,qrtz_fired_triggers,qrtz_job_details。
六、简单调度示例
- 计划任务类
@Component
@Slf4j
public class ScheduleTask extends QuartzJobBean {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info("当前时间是: " + dateFormat.format(new Date()));
}
}
- 计划任务配置
@Configuration
public class ScheduleConfiguration {
@Bean
public JobDetail scheduleJob() {
// 添加已定义的计划任务
return JobBuilder.newJob(ScheduleTask.class)
.storeDurably() //
// 可以给该JobDetail起一个id,便于之后的检索。也可以 .withIdentity("myjob", "group1")
.withIdentity("sample_schedule") //即使没有Trigger关联时,也不需要删除该JobDetail
.withDescription("Sample schedule task") // 描述信息
.build();
}
@Bean
public Trigger scheduleTrigger() {
return newTrigger()
.withIdentity("trigger")
.forJob(scheduleJob())
// cron 表达式,每分钟执行一次
.withSchedule(CronScheduleBuilder.cronSchedule("0 * * * * ?"))
.build();
}
}
-
测试
启动程序:
六、动态计划任务
- 创建表
DROP TABLE IF EXISTS `task_schedule_job`;
CREATE TABLE `task_schedule_job` (
`id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`cron_expression` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'cron表达式',
`method_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务调用的方法名',
`method_params` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '请求参数',
`misfire_policy` varchar(4) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '执行策略',
`is_concurrent` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '任务是否有状态',
`job_status` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '任务状态',
`job_group` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '任务分组',
`execute_class` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Spring bean',
`job_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '任务名',
`load_way` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '加载任务方式',
`description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '任务描述',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建者',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '更新者',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
DROP TABLE IF EXISTS `task_schedule_job_log`;
CREATE TABLE `task_schedule_job_log` (
`id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务日志ID',
`job_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务名称',
`job_group` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务组名',
`execute_class` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '执行类名',
`method_name` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '任务方法',
`method_params` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '方法参数',
`job_message` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '日志信息',
`status` char(2) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '执行状态(0普通 1成功 -1失败)',
`exception_info` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '异常信息',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '定时任务调度日志表' ROW_FORMAT = Dynamic;
- 创建实体类
任务类:ScheduleJob
@Data
@Accessors(chain = true)
@TableName("task_schedule_job")
public class ScheduleJob {
/** 任务主键 */
@TableId(value = "id", type = IdType.UUID)
private String id;
/** 任务名 */
@TableField(value = "job_name")
private String jobName;
/** cron表达式 */
@TableField(value = "cron_expression")
private String cronExpression;
/** Spring bean */
@TableField(value = "execute_class")
private String executeClass;
/** 任务调用的方法名 */
@TableField(value = "method_name")
private String methodName;
/**
* 任务调用的参数
*/
@TableField(value = "method_params")
private String methodParams;
/**
* 执行策略
*/
@TableField(value = "misfire_policy")
private String misfirePolicy;
@TableField(value = "load_way")
private String loadWay;
/** 任务是否有状态 */
@TableField(value = "is_concurrent")
private String isConcurrent;
/** 任务描述 */
@TableField(value = "description")
private String description;
/** 更新者 */
@TableField(value = "update_by")
private String updateBy;
/** 创建时间 */
@TableField(value = "create_time")
@JSONField(format="yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
/** 任务状态 */
@TableField(value = "job_status")
private String jobStatus;
/** 任务分组 */
@TableField(value = "job_group")
private String jobGroup;
/** 更新时间 */
@TableField(value = "update_time")
@JSONField(format="yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
/** 创建者 */
@TableField(value = "create_by")
private String createBy;
}
日志类:
@Data
@TableName("task_schedule_job_log")
public class ScheduleJobLog {
/**
* 成功
*/
public static final String SCHEDULE_JOB_LOG_RUN_SUCCESS = "1";
/**
* 正常
*/
public static final String SCHEDULE_JOB_LOG_RUN_NOMAL = "0";
/**
* 失败
*/
public static final String SCHEDULE_JOB_LOG_RUN_FAIL = "-1";
/**任务日志ID*/
@TableId(value = "id", type = IdType.UUID)
private String id;
/**任务名称*/
@TableField(value = "job_name")
private String jobName;
/**任务组名*/
@TableField(value = "job_group")
private String jobGroup;
/** Spring bean */
@TableField(value = "execute_class")
private String executeClass;
/**任务方法*/
@TableField(value = "method_name")
private String methodName;
/**方法参数*/
@TableField(value = "method_params")
private String methodParams;
/**日志信息*/
@TableField(value = "job_message")
private String jobMessage;
/**执行状态(0正常 1失败)*/
@TableField(value = "status")
private String status;
/**异常信息*/
@TableField(value = "exception_info")
private String exceptionInfo;
/**创建时间*/
@TableField(value = "create_time")
@JSONField(format="yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
}
数据类:
@Data
public class ScheduleJob implements Serializable {
private static final long serialVersionUID = 1L;
private String jobId;
/**
* 任务名称
*/
private String jobName;
/**
* 任务分组
*/
private String jobGroup;
/**
* 任务状态 是否启动任务
*/
private String jobStatus;
/**
* cron表达式
*/
private String cronExpression;
/**
* 类加载方式
*/
private String loadWay;
/**
* 描述
*/
private String description;
/**
* 任务是否有状态
*/
private String isConcurrent;
/**
* 任务调用的方法名
*/
private String methodName;
/**
* 任务调用的参数
*/
private String methodParams;
/**
* 执行策略
*/
private String misfirePolicy;
/**
* 执行类
*/
private String executeClass;
}
- 任务常量类
作用:用于定义生成键
public interface ScheduleConstants {
String TASK_JOB_KEY_PRE_ = "TASK_JOB_KEY_PRE_";
/**
* 触发器键
*/
String TASK_TRIGGER_KEY_PRE_ = "TASK_TRIGGER_KEY_PRE_";
/**
* 任务Bean键
*/
String TASK_JOB_BEAN_KEY = "TASK_JOB_BEAN_KEY";
/**
* 默认策略
*/
String MISFIRE_DEFAULT = "0";
/**
* 立即触发执行
*/
String MISFIRE_IGNORE_MISFIRES = "1";
/**
* 触发一次执行
*/
String MISFIRE_FIRE_AND_PROCEED = "2";
/**
* 不触发立即执行
*/
String MISFIRE_DO_NOTHING = "3";
String STATUS_RUNNING = "1";
String STATUS_NOT_RUNNING = "0";
String CONCURRENT_IS = "1";
String CONCURRENT_NOT = "0";
}
- 计划任务工厂类
作用:执行任务
@Slf4j
public class QuartzJobFactory extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
log.info(context.getMergedJobDataMap().get(ScheduleConstants.TASK_JOB_BEAN_KEY).toString());
// 根据键名获取计划任务
ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get(ScheduleConstants.TASK_JOB_BEAN_KEY);
// 任务回调,主要用来记录日志
QuartzExecuteCallback quartzExecuteCallback = SpringContextHolder.getBean(QuartzExecuteCallback.class);
long startTime = System.currentTimeMillis();
try {
// TODO 1.开始执行日志
quartzExecuteCallback.onStart(scheduleJob);
// 执行任务
log.info("任务开始执行 - 名称:{} 方法:{}", scheduleJob.getJobName(), scheduleJob.getMethodName());
ScheduleJobinvoke.invokeMethod(scheduleJob);
long times = System.currentTimeMillis() - startTime;
String message = scheduleJob.getJobName() + " 总共耗时:" + times + "毫秒";
// TODO 2.执行成功日志
quartzExecuteCallback.onSuccess(scheduleJob, message);
log.info("任务执行结束 - 名称:{} 耗时:{} 毫秒", scheduleJob.getJobName(), times);
} catch (Exception e) {
log.info("任务执行失败 - 名称:{} 方法:{}", scheduleJob.getJobName(), scheduleJob.getMethodName());
log.error("任务执行异常 - :", e);
long times = System.currentTimeMillis() - startTime;
String message = scheduleJob.getJobName() + " 总共耗时:" + times + "毫秒";
// TODO 3.执行失败日志
quartzExecuteCallback.onFailure(scheduleJob, e, message);
}
}
}
- 计划任务反射类
public class ScheduleJobinvoke {
/**
* 通过反射调用scheduleJob中定义的方法
*
* @param scheduleJob
*/
public static void invokeMethod(ScheduleJob scheduleJob) {
try {
Object object = null;
Class<?> clazz = null;
Method method = null;
// 类加载方式
if (scheduleJob.getLoadWay().equals("1")) {
object = SpringContextHolder.getBean(scheduleJob.getExecuteClass());
} else if (StringUtils.isNotBlank(scheduleJob.getExecuteClass())) {
clazz = Class.forName(scheduleJob.getExecuteClass());
object = clazz.newInstance();
}
if (object == null) {
throw new QuartzException("任务对象不存在");
}
clazz = object.getClass();
method = clazz.getDeclaredMethod(scheduleJob.getMethodName());
if (method != null) {
String params = scheduleJob.getMethodParams();
ReflectionUtils.makeAccessible(method);
// 执行方法,有参与无参
if (StringUtils.isNotEmpty(params))
{
method.invoke(object, params);
}else
{
method.invoke(object);
}
}
} catch (InstantiationException e) {
throw new QuartzException(e.getMessage());
} catch (ClassNotFoundException e) {
throw new QuartzException(e.getMessage());
} catch (NoSuchMethodException e) {
throw new QuartzException(e.getMessage());
} catch (SecurityException e) {
throw new QuartzException(e.getMessage());
} catch (IllegalAccessException e) {
throw new QuartzException(e.getMessage());
} catch (IllegalArgumentException e) {
throw new QuartzException(e.getMessage());
} catch (InvocationTargetException e) {
throw new QuartzException(e.getMessage());
}
}
}
- 禁用并发接口
@DisallowConcurrentExecution
public class QuartzJobFactoryDisallowConcurrentExecution extends QuartzJobFactory {
}
@DisallowConcurrentExecution 注解
Quartz定时任务默认都是并发执行的,不会等待上一次任务执行完毕,只要间隔时间到就会执行, 如果定时任执行太长,会长时间占用资源,导致其它任务堵塞。
@DisallowConcurrentExecution 禁止并发执行多个相同定义的JobDetail, 这个注解是加在Job类上的, 但意思并不是不能同时执行多个Job, 而是不能并发执行同一个Job Definition(由JobDetail定义), 但是可以同时执行多个不同的JobDetail。
@PersistJobDataAfterExecution 同样, 也是加在Job上,表示当正常执行完Job后, JobDataMap中的数据应该被改动, 以被下一次调用时用。当使用@PersistJobDataAfterExecution 注解时, 为了避免并发时, 存储数据造成混乱。
- 任务管理器
@Slf4j
@Component
public class QuartzManager {
/**
* 获取触发器key
*/
public static TriggerKey getTriggerKey(ScheduleJob job) {
return TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
}
/**
* 获取jobKey
*/
public static JobKey getJobKey(ScheduleJob job) {
return JobKey.jobKey(job.getJobName(), job.getJobGroup());
}
public static CronScheduleBuilder handleCronScheduleMisfirePolicy(ScheduleJob scheduleJob, CronScheduleBuilder cb)
throws QuartzException {
String misfirePolicy = scheduleJob.getMisfirePolicy();
if (StringUtils.isEmpty(misfirePolicy)) {
misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT;
}
switch (misfirePolicy) {
case ScheduleConstants.MISFIRE_DEFAULT:
return cb;
case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
return cb.withMisfireHandlingInstructionIgnoreMisfires();
case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
return cb.withMisfireHandlingInstructionFireAndProceed();
case ScheduleConstants.MISFIRE_DO_NOTHING:
return cb.withMisfireHandlingInstructionDoNothing();
default:
throw new QuartzException("这个任务规则无效 '" + scheduleJob.getMisfirePolicy()
+ "不能在cron计划任务中使用");
}
}
/**
* 添加任务
*
* @param job
* @throws SchedulerException
*/
public void addJob(ScheduleJob job) throws SchedulerException {
if (job == null || !ScheduleConstants.STATUS_RUNNING.equals(job.getJobStatus())) {
return;
}
// 获取调度工厂Bean
Scheduler scheduler = SpringContextHolder.getBean(SchedulerFactoryBean.class).getScheduler();
log.debug(scheduler + "...................................................add");
TriggerKey triggerKey = this.getTriggerKey(job);
// 调度器中获取触发器
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// TODO 1. 不存在,创建一个
if (null == trigger) {
// 如果任务就同步获取QuartzJobFactory 对象,如果不是同步QuartzJobFactoryDisallowConcurrentExecution
Class<? extends Job> clazz = ScheduleConstants.CONCURRENT_IS.equals(job.getIsConcurrent())
? QuartzJobFactory.class : QuartzJobFactoryDisallowConcurrentExecution.class;
// 创建对象
JobDetail jobDetail = JobBuilder.newJob(clazz)
.storeDurably()
.requestRecovery()
.withIdentity(getJobKey(job))
.withDescription(job.getDescription())
.build();
jobDetail.getJobDataMap()
.put(ScheduleConstants.TASK_JOB_BEAN_KEY, job);
// 使用Cron表达式
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
// 执行策略
scheduleBuilder = handleCronScheduleMisfirePolicy(job, scheduleBuilder);
// 使用触发器
trigger = TriggerBuilder
.newTrigger()
.withIdentity(getTriggerKey(job))
.withSchedule(scheduleBuilder)
.build();
scheduler.scheduleJob(jobDetail, trigger);
} else {
// TODO 2.Trigger已存在,那么更新相应的定时设置
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
// TODO 3.执行策略
scheduleBuilder = handleCronScheduleMisfirePolicy(job, scheduleBuilder);
// TODO 4.按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
// TODO 5.按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);
}
// TODO 6.暂停任务
if (job.equals(ScheduleConstants.STATUS_NOT_RUNNING)) {
pauseJob(job);
}
}
/**
* 获取所有计划中的任务列表
*
* @return
* @throws SchedulerException
*/
public List<ScheduleJob> getAllJob() throws SchedulerException {
Scheduler scheduler = SpringContextHolder.getBean(SchedulerFactoryBean.class).getScheduler();
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
for (JobKey jobKey : jobKeys) {
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
for (Trigger trigger : triggers) {
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
ScheduleJob job = (ScheduleJob) jobDetail.getJobDataMap().get(ScheduleConstants.TASK_JOB_BEAN_KEY);
Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
job.setJobStatus(triggerState.name());
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
String cronExpression = cronTrigger.getCronExpression();
job.setCronExpression(cronExpression);
}
jobList.add(job);
}
}
return jobList;
}
/**
* 所有正在运行的job
*
* @return
* @throws SchedulerException
*/
public List<ScheduleJob> getRunningJob() throws SchedulerException {
Scheduler scheduler = SpringContextHolder.getBean(SchedulerFactoryBean.class).getScheduler();
List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());
for (JobExecutionContext executingJob : executingJobs) {
JobDetail jobDetail = executingJob.getJobDetail();
JobKey jobKey = jobDetail.getKey();
Trigger trigger = executingJob.getTrigger();
ScheduleJob job = (ScheduleJob) jobDetail.getJobDataMap().get(ScheduleConstants.TASK_JOB_BEAN_KEY);
Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
job.setJobStatus(triggerState.name());
if (trigger instanceof CronTrigger) {
CronTrigger cronTrigger = (CronTrigger) trigger;
String cronExpression = cronTrigger.getCronExpression();
job.setCronExpression(cronExpression);
}
jobList.add(job);
}
return jobList;
}
/**
* 暂停一个job
*
* @param scheduleJob
* @throws SchedulerException
*/
public void pauseJob(ScheduleJob scheduleJob) throws SchedulerException {
Scheduler scheduler = SpringContextHolder.getBean(SchedulerFactoryBean.class).getScheduler();
JobKey jobKey = getJobKey(scheduleJob);
scheduler.pauseJob(jobKey);
}
/**
* 恢复一个job
*
* @param scheduleJob
* @throws SchedulerException
*/
public void resumeJob(ScheduleJob scheduleJob) throws SchedulerException {
Scheduler scheduler = SpringContextHolder.getBean(SchedulerFactoryBean.class).getScheduler();
JobKey jobKey = getJobKey(scheduleJob);
scheduler.resumeJob(jobKey);
}
/**
* 删除一个job
*
* @param scheduleJob
* @throws SchedulerException
*/
public void deleteJob(ScheduleJob scheduleJob) throws SchedulerException {
Scheduler scheduler = SpringContextHolder.getBean(SchedulerFactoryBean.class).getScheduler();
JobKey jobKey = getJobKey(scheduleJob);
scheduler.deleteJob(jobKey);
}
/**
* 立即执行job
*
* @param scheduleJob
* @throws SchedulerException
*/
public void runAJobNow(ScheduleJob scheduleJob) throws SchedulerException {
Scheduler scheduler = SpringContextHolder.getBean(SchedulerFactoryBean.class).getScheduler();
JobKey jobKey = getJobKey(scheduleJob);
scheduler.triggerJob(jobKey);
}
/**
* 更新job时间表达式
*
* @param scheduleJob
* @throws SchedulerException
*/
public void updateJobCron(ScheduleJob scheduleJob) throws SchedulerException {
Scheduler scheduler = SpringContextHolder.getBean(SchedulerFactoryBean.class).getScheduler();
TriggerKey triggerKey = getTriggerKey(scheduleJob);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());
// 执行策略
scheduleBuilder = handleCronScheduleMisfirePolicy(scheduleJob, scheduleBuilder);
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
// 参数
trigger.getJobDataMap().put(ScheduleConstants.TASK_JOB_BEAN_KEY, scheduleJob);
scheduler.rescheduleJob(triggerKey, trigger);
// 暂停任务
if (scheduleJob.getJobStatus().equals(ScheduleConstants.STATUS_NOT_RUNNING)) {
pauseJob(scheduleJob);
}
}
/**
* 清空任务
*
* @throws SchedulerException
*/
public void empty() throws SchedulerException {
Scheduler scheduler = SpringContextHolder.getBean(SchedulerFactoryBean.class).getScheduler();
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
scheduler.deleteJobs(Lists.newArrayList(jobKeys));
}
}
- 自定义异常
public class QuartzException extends RuntimeException {
public QuartzException() {
super();
}
public QuartzException(String message) {
super(message);
}
public QuartzException(String message, Throwable cause) {
super(message, cause);
}
public QuartzException(Throwable cause) {
super(cause);
}
protected QuartzException(String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
- 初始化接口
public interface QuartzInitCallback {
void initSchedule();
}
- 记录日志回调类
作用:用于记录日志
public interface QuartzExecuteCallback {
/**
* 执行开始
* @param scheduleJob
*/
void onStart(ScheduleJob scheduleJob);
/**
* 当执行成功
* @param scheduleJob
* @param message
*/
void onSuccess(ScheduleJob scheduleJob, String message);
/**
* 执行失败
* @param scheduleJob
* @param e
* @param message
*/
void onFailure(ScheduleJob scheduleJob, Exception e, String message);
}
- 业务接口
1)接口
public interface IScheduleJobService extends IService<ScheduleJob> {
/**
* 初始化任务
*/
void initSchedule();
/**
* 更改任务
*
* @param jobId
* @param cmd
*/
void changeStatus(String jobId, String cmd);
/**
* 更改任务 cron表达式
*/
void updateCron(String jobId);
/**
* 执行一次
*/
void runAJobNow(String jobId);
/**
* 刷新任务
*/
void refreshTask();
/**
* 删除任务
* @param jobId
* @return
*/
boolean deleteById(String jobId);
/**
* 获取任务分页数据
* @param jobRequest
* @return
*/
IPage<ScheduleJob> getListByJobName(JobRequest jobRequest);
/**
* 添加任务
* @param entity
*/
void add(ScheduleJob entity);
}
2)实现类
@Slf4j
@Service
public class IScheduleJobServiceImpl extends ServiceImpl<ScheduleJobMapper, ScheduleJob> implements IScheduleJobService, QuartzInitCallback {
@Autowired
private ScheduleJobMapper scheduleJobMapper;
@Autowired
private QuartzManager quartzManager;
@Override
public void initSchedule() {
try {
QueryWrapper<ScheduleJob> queryWrapper = new QueryWrapper<>();
// 这里获取任务信息数据
List<ScheduleJob> jobList = this.list(queryWrapper);
for (ScheduleJob scheduleJob : jobList) {
quartzManager.addJob(ScheduleJobUtils.entityToData(scheduleJob));
}
} catch (SchedulerException e) {
throw new RuntimeException(e.getMessage());
}
}
@Override
public void changeStatus(String jobId, String cmd) {
try {
ScheduleJob scheduleJob = this.getById(jobId);
if (scheduleJob == null) {
return;
}
if ("stop".equals(cmd)) {
quartzManager.deleteJob(ScheduleJobUtils.entityToData(scheduleJob));
scheduleJob.setJobStatus(ScheduleConstants.STATUS_NOT_RUNNING);
} else if ("start".equals(cmd)) {
scheduleJob.setJobStatus(ScheduleConstants.STATUS_RUNNING);
// 添加对象
quartzManager.addJob(ScheduleJobUtils.entityToData(scheduleJob));
}
// 更新数据库
this.updateById(scheduleJob);
} catch (SchedulerException e) {
throw new RuntimeException(e.getMessage());
}
}
@Override
public void updateCron(String jobId) {
ScheduleJob scheduleJob = this.getById(jobId);
if (scheduleJob == null) {
return;
}
try {
// 更新任务
quartzManager.updateJobCron(ScheduleJobUtils.entityToData(scheduleJob));
// 更新数据据
this.updateById(scheduleJob);
} catch (SchedulerException e) {
throw new RuntimeException(e.getMessage());
}
}
@Override
public void runAJobNow(String jobId) {
try {
ScheduleJob scheduleJob = this.getById(jobId);
quartzManager.runAJobNow(ScheduleJobUtils.entityToData(scheduleJob));
} catch (SchedulerException e) {
throw new RuntimeException(e.getMessage());
}
}
@Override
public void refreshTask() {
try {
quartzManager.empty();
// 实始化任务
initSchedule();
} catch (SchedulerException e) {
throw new RuntimeException(e.getMessage());
}
}
@Override
public boolean deleteById(String jobId) {
try {
ScheduleJob scheduleJob = this.getById(jobId);
quartzManager.deleteJob(ScheduleJobUtils.entityToData(scheduleJob));
} catch (SchedulerException e) {
throw new RuntimeException(e.getMessage());
}
// 删除数据库
return this.deleteById(jobId);
}
@Override
public IPage<ScheduleJob> getListByJobName(JobRequest jobRequest) {
QueryWrapper<ScheduleJob> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("create_time");
if (StringUtils.isNotEmpty(jobRequest.getJobName())) {
queryWrapper.like("jobName", jobRequest.getJobName());
}
Page page = new Page(jobRequest.getCurrentPage(), jobRequest.getPageSize());
IPage<ScheduleJob> iPage = this.page(page, queryWrapper);
return iPage;
}
@Override
public void add(ScheduleJob entity) {
this.save(entity);
}
}
- 任务日志接口与实现类
1)接口
public interface IScheduleJobLogService extends IService<ScheduleJobLog> {
/**
* @return
*/
IPage<ScheduleJobLog> getListByJobNameAndExecuteClassAndStatus(ScheduleJobLogRequest scheduleJobLogRequest);
}
2)实现类
@Slf4j
@Service
public class IScheduleJobLogServiceImpl extends ServiceImpl<ScheduleJobLogMapper, ScheduleJobLog> implements IScheduleJobLogService, QuartzExecuteCallback {
@Autowired
private ScheduleJobLogMapper scheduleJobLogMapper;
@Override
public void onStart(ScheduleJob scheduleJob) {
ScheduleJobLog scheduleJobLog = newJobLog(scheduleJob);
scheduleJobLog.setJobMessage(scheduleJob.getJobName() + "运行开始!");
scheduleJobLog.setStatus(ScheduleJobLog.SCHEDULE_JOB_LOG_RUN_NOMAL);
this.save(scheduleJobLog);
}
@Override
public void onSuccess(ScheduleJob scheduleJob, String message) {
ScheduleJobLog scheduleJobLog = newJobLog(scheduleJob);
scheduleJobLog.setJobMessage(message);
scheduleJobLog.setStatus(ScheduleJobLog.SCHEDULE_JOB_LOG_RUN_SUCCESS);
this.save(scheduleJobLog);
}
@Override
public void onFailure(ScheduleJob scheduleJob, Exception e, String message) {
ScheduleJobLog scheduleJobLog = newJobLog(scheduleJob);
scheduleJobLog.setJobMessage(message);
scheduleJobLog.setStatus(ScheduleJobLog.SCHEDULE_JOB_LOG_RUN_FAIL);
scheduleJobLog.setExceptionInfo(e.getMessage());
this.save(scheduleJobLog);
}
/**
* 进行数据类型转换
* @param scheduleJob
* @return
*/
private ScheduleJobLog newJobLog(ScheduleJob scheduleJob){
ScheduleJobLog scheduleJobLog = new ScheduleJobLog();
scheduleJobLog.setExecuteClass(scheduleJob.getExecuteClass());
scheduleJobLog.setJobName(scheduleJob.getJobName());
scheduleJobLog.setJobGroup(scheduleJob.getJobGroup());
scheduleJobLog.setMethodName(scheduleJob.getMethodName());
scheduleJobLog.setMethodParams(scheduleJob.getMethodParams());
scheduleJobLog.setCreateTime(new Date());
return scheduleJobLog;
}
@Override
public IPage<ScheduleJobLog> getListByJobNameAndExecuteClassAndStatus(ScheduleJobLogRequest scheduleJobLogRequest) {
QueryWrapper<ScheduleJobLog> queryWrapper=new QueryWrapper<>();
queryWrapper.orderByDesc("create_time");
if(StringUtils.isNotEmpty(scheduleJobLogRequest.getJobName())) {
queryWrapper.like("jobName", scheduleJobLogRequest.getJobName());
}
if(StringUtils.isNotEmpty(scheduleJobLogRequest.getExecuteClass())) {
queryWrapper.like("executeClass", scheduleJobLogRequest.getExecuteClass());
}
if(StringUtils.isNotEmpty(scheduleJobLogRequest.getStatus())) {
queryWrapper.like("status", scheduleJobLogRequest.getStatus());
}
Page page=new Page(scheduleJobLogRequest.getCurrentPage(),scheduleJobLogRequest.getPageSize());
return this.page(page,queryWrapper);
}
}
七、测试
- 创建任务类
@Component("testTask")
@Slf4j
public class TestTask {
public void run() {
log.info("任务测试");
}
}
- 控制器类
作用:使用web服务器控制器类测试
@RestController
@RequestMapping(value = "/task/job", produces = {"application/json;charset=UTF-8"})
@Slf4j
@Validated
@Api(tags = {"计划任务"})
public class ScheduleJobController {
@Autowired
private IScheduleJobService scheduleJobService;
@GetMapping(value = "list")
@Log(logType = LogType.SELECT)
@ApiOperation(value = "1、获取任务列表", notes = "获取任务列表", httpMethod = "GET")
public Result list(JobRequest jobRequest) throws IOException {
IPage<ScheduleJob> iPage = scheduleJobService.getListByJobName(jobRequest);
return Result.success(iPage);
}
@PostMapping("add")
@Log(logType = LogType.INSERT)
@ApiOperation(value = "2、添加任务", notes = "添加任务", httpMethod = "POST")
public Result add(@RequestBody ScheduleJob entity) {
scheduleJobService.add(entity);
return Result.success("添加成功");
}
@PutMapping("{id}")
@Log(logType = LogType.UPDATE)
@ApiOperation(value = "3、更新任务", notes = "更新任务", httpMethod = "PUT")
public Result update(@PathVariable("id") String id,@RequestBody ScheduleJob entity) {
entity.setId(id);
scheduleJobService.updateById(entity);
return Result.success("更新成功");
}
@DeleteMapping("{id}")
@Log(logType = LogType.DELETE)
@ApiOperation(value = "4、删除任务", notes = "删除任务", httpMethod = "DELETE")
public Result delete(@PathVariable("id") String id) {
scheduleJobService.deleteById(id);
return Result.success("删除成功");
}
@DeleteMapping("batch")
@Log(logType = LogType.DELETE)
@ApiOperation(value = "5、批量删除任务", notes = "批量删除任务", httpMethod = "DELETE")
public Result batchDelete(@RequestBody List<String> ids) {
scheduleJobService.removeByIds(ids);
return Result.success("删除成功");
}
@PostMapping(value = "jobStatus")
@Log(logType = LogType.OTHER, title = "执行任务")
@ApiOperation(value = "6、执行任务", notes = "执行任务", httpMethod = "POST")
public Result changeJobStatus(@RequestBody JobStatusRequest jobStatusRequest) {
String label = "停止";
if (jobStatusRequest.getCmd().equals("start")) {
label = "启动";
} else {
label = "停止";
}
try {
// TODO 改变任务状态
scheduleJobService.changeStatus(jobStatusRequest.getId(), jobStatusRequest.getCmd());
} catch (Exception e) {
e.printStackTrace();
return Result.fail("任务" + label + "失败" + e.getMessage());
}
return Result.success("任务" + label + "成功");
}
@PostMapping(value = "{id}/jobCron")
@Log(logType = LogType.OTHER, title = "任务更新")
@ApiOperation(value = "7、任务更新", notes = "任务更新", httpMethod = "POST")
public Result updateCron(@PathVariable("id") String id) {
scheduleJobService.updateCron(id);
return Result.success("任务更新成功");
}
@PostMapping(value = "/runAJobNow")
@Log(logType = LogType.OTHER, title = "执行一次")
@ApiOperation(value = "8、执行一次", notes = "执行一次", httpMethod = "POST")
public Result runAJobNow(@RequestBody ScheduleJob scheduleJob) {
scheduleJobService.runAJobNow(scheduleJob.getId());
return Result.success("任务启动成功");
}
/**
* 刷新任务
*
* @return
*/
@PostMapping(value = "/refreshJob")
@Log(logType = LogType.OTHER, title = "刷新任务")
@ApiOperation(value = "9、刷新任务", notes = "刷新任务", httpMethod = "POST")
public Result refreshJob() {
scheduleJobService.refreshTask();
return Result.success("刷新任务成功");
}
}
八、常见问题:
- referenced by the trigger does not exist.
需要自己创建表,请执行数据库脚本。 - 定时任务 quartz中出现相同类型的对象无法转换问题(java.lang.ClassCastException)
项目中应该是采用了热部署,devtools,因为类加载器的不同所以会导致类型转换失败;
在依赖中去掉:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
依赖。