首先在你的微服务项目中创建一个新的模块,定时调度模块
pom.xml里面关联公共模块common的依赖其他不需要改变
然后启动类别删,启动项目是否报错,写一个简单的测试类访问路径是否成功
package com.jiawa.train.batch.controller;
import com.jiawa.train.batch.feign.BusinessFeign;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Resource
BusinessFeign businessFeign;
@GetMapping("/hello")
public String hello() {
return "Hello World! Batch!";
}
}
创建一个包job 创建类 SpringBootTestJob.java
package com.jiawa.train.batch.job;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 适合单体应用,不适合集群
* 没法实时更改定时任务状态和策略
*/
@Component
@EnableScheduling // 开启定时任务
public class SpringBootTestJob {
@Scheduled(cron = "0/5 * * * * ?")// 每5秒执行一次 每5秒执行一次 cron 从左到右空格隔开:秒 分钟 小时 日期 月份 星期
private void test() {
// 增加分布式锁,解决集群问题
System.out.println("SpringBootTestJob TEST");
}
}
多个定时任务就写多个@Scheduled, 定时任务的三要素也要知道:1. 执行的内容:功能逻辑 2. 执行的策略: cron表达式 3. 开关: 开启定时任务
SpringBoot自带的定时任务缺点: 无法实时更改任务状态,就是当我想把定时任务暂停一下今天的数据有问题,但是这个就无法进行暂停所以用专业的任务调度的框架。
SpringBoot自带的定时任务优点: 开发速度快适合小型的项目,两个注解就好了
首先导入依赖pom.xml。 这里没有版本是因为我的这个是子模块 版本由父模块控制
org.springframework.boot
spring-boot-starter-quartz
跟着思路来导入了依赖之后我们就开始声明我们的任务了, 创建一个配置类 quartzConfig 触发器就是调用前面声明的任务方法来定时
package com.jiawa.train.batch.config;
import com.jiawa.train.batch.job.TestJob;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuartzConfig {
/**
* 声明一个任务
* @return
*/
@Bean
public JobDetail jobDetail() {
return JobBuilder.newJob(TestJob.class)
.withIdentity("TestJob", "test")// 任务名称和组构成任务key
.storeDurably()
.build();
}
/**
* 声明一个触发器,什么时候触发这个任务
* @return
*/
@Bean
public Trigger trigger() {
return TriggerBuilder.newTrigger()// 创建一个新的触发器
.forJob(jobDetail()) // 将任务detail作为触发器要触发的任务
.withIdentity("trigger", "trigger") // 为触发器设置一个唯一标识,例如 "trigger" 和 "trigger"
.startNow() // 立即触发任务,从当前时间开始
.withSchedule(CronScheduleBuilder.cronSchedule("*/2 * * * * ?")) // 设置触发器的调度规则,使用 cron 格式的字符串,表示每隔 2 分钟触发一次任务
.build();
}
}
上面代码写了注释的,任务名称TestJob这里就创建一个任务来对应,实现的是Job是框架quartz里面的接口
@DisallowConcurrentExecution // 禁止并发执行
package com.jiawa.train.batch.job;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
@DisallowConcurrentExecution // 禁止并发执行
public class TestJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("TestJob TEST开始");
// try {
// Thread.sleep(3000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println("TestJob TEST结束");
}
}
这就集成了框架的使用,springboot自带的定时任务也在使用。前面是简单的使用
数据库配置quartz框架调度任务,官方提供的数据库MySQL,创建这几张表记得分库
#
# Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar
#
# PLEASE consider using mysql with innodb tables to avoid locking issues
#
# In your Quartz properties file, you'll need to set
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#
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 comment '定时任务名称',
JOB_NAME VARCHAR(200) NOT NULL comment 'job名称',
JOB_GROUP VARCHAR(200) NOT NULL comment 'job组',
DESCRIPTION VARCHAR(250) NULL comment '描述',
JOB_CLASS_NAME VARCHAR(250) NOT NULL comment 'job类名',
IS_DURABLE VARCHAR(1) NOT NULL comment '是否持久化',
IS_NONCONCURRENT VARCHAR(1) NOT NULL comment '是否非同步',
IS_UPDATE_DATA VARCHAR(1) NOT NULL comment '是否更新数据',
REQUESTS_RECOVERY VARCHAR(1) NOT NULL comment '请求是否覆盖',
JOB_DATA BLOB NULL comment 'job数据',
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);
CREATE TABLE QRTZ_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL comment '定时任务名称',
TRIGGER_NAME VARCHAR(200) NOT NULL comment '触发器名称',
TRIGGER_GROUP VARCHAR(200) NOT NULL comment '触发器组',
JOB_NAME VARCHAR(200) NOT NULL comment 'job名称',
JOB_GROUP VARCHAR(200) NOT NULL comment 'job组',
DESCRIPTION VARCHAR(250) NULL comment '描述',
NEXT_FIRE_TIME BIGINT(13) NULL comment '下一次触发时间',
PREV_FIRE_TIME BIGINT(13) NULL comment '前一次触发时间',
PRIORITY INTEGER NULL comment '等级',
TRIGGER_STATE VARCHAR(16) NOT NULL comment '触发状态',
TRIGGER_TYPE VARCHAR(8) NOT NULL comment '触发类型',
START_TIME BIGINT(13) NOT NULL comment '开始时间',
END_TIME BIGINT(13) NULL comment '结束时间',
CALENDAR_NAME VARCHAR(200) NULL comment '日程名称',
MISFIRE_INSTR SMALLINT(2) NULL comment '未触发实例',
JOB_DATA BLOB NULL comment 'job数据',
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)
);
CREATE TABLE QRTZ_SIMPLE_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL comment '定时任务名称',
TRIGGER_NAME VARCHAR(200) NOT NULL comment '触发器名称',
TRIGGER_GROUP VARCHAR(200) NOT NULL comment '触发器组',
REPEAT_COUNT BIGINT(7) NOT NULL comment '重复执行次数',
REPEAT_INTERVAL BIGINT(12) NOT NULL comment '重复执行间隔',
TIMES_TRIGGERED BIGINT(10) NOT NULL comment '已经触发次数',
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)
);
CREATE TABLE QRTZ_CRON_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL comment '定时任务名称',
TRIGGER_NAME VARCHAR(200) NOT NULL comment '触发器名称',
TRIGGER_GROUP VARCHAR(200) NOT NULL comment '触发器组',
CRON_EXPRESSION VARCHAR(200) NOT NULL comment 'cron表达式',
TIME_ZONE_ID VARCHAR(80) comment '时区',
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)
);
CREATE TABLE QRTZ_SIMPROP_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL comment '定时任务名称',
TRIGGER_NAME VARCHAR(200) NOT NULL comment '触发器名称',
TRIGGER_GROUP VARCHAR(200) NOT NULL comment '触发器组',
STR_PROP_1 VARCHAR(512) NULL comment '开始配置1',
STR_PROP_2 VARCHAR(512) NULL comment '开始配置2',
STR_PROP_3 VARCHAR(512) NULL comment '开始配置3',
INT_PROP_1 INT NULL comment 'int配置1',
INT_PROP_2 INT NULL comment 'int配置2',
LONG_PROP_1 BIGINT NULL comment 'long配置1',
LONG_PROP_2 BIGINT NULL comment 'long配置2',
DEC_PROP_1 NUMERIC(13,4) NULL comment '配置描述1',
DEC_PROP_2 NUMERIC(13,4) NULL comment '配置描述2',
BOOL_PROP_1 VARCHAR(1) NULL comment 'bool配置1',
BOOL_PROP_2 VARCHAR(1) NULL comment 'bool配置2',
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)
);
CREATE TABLE QRTZ_BLOB_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL comment '定时任务名称',
TRIGGER_NAME VARCHAR(200) NOT NULL comment '触发器名称',
TRIGGER_GROUP VARCHAR(200) NOT NULL comment '触发器组',
BLOB_DATA BLOB NULL comment '数据',
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)
);
CREATE TABLE QRTZ_CALENDARS
(
SCHED_NAME VARCHAR(120) NOT NULL comment '定时任务名称',
CALENDAR_NAME VARCHAR(200) NOT NULL comment '日程名称',
CALENDAR BLOB NOT NULL comment '日程数据',
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);
CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
(
SCHED_NAME VARCHAR(120) NOT NULL comment '定时任务名称',
TRIGGER_GROUP VARCHAR(200) NOT NULL comment '触发器组',
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);
CREATE TABLE QRTZ_FIRED_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL comment '定时任务名称',
ENTRY_ID VARCHAR(95) NOT NULL comment 'entryId',
TRIGGER_NAME VARCHAR(200) NOT NULL comment '触发器名称',
TRIGGER_GROUP VARCHAR(200) NOT NULL comment '触发器组',
INSTANCE_NAME VARCHAR(200) NOT NULL comment '实例名称',
FIRED_TIME BIGINT(13) NOT NULL comment '执行时间',
SCHED_TIME BIGINT(13) NOT NULL comment '定时任务时间',
PRIORITY INTEGER NOT NULL comment '等级',
STATE VARCHAR(16) NOT NULL comment '状态',
JOB_NAME VARCHAR(200) NULL comment 'job名称',
JOB_GROUP VARCHAR(200) NULL comment 'job组',
IS_NONCONCURRENT VARCHAR(1) NULL comment '是否异步',
REQUESTS_RECOVERY VARCHAR(1) NULL comment '是否请求覆盖',
PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);
CREATE TABLE QRTZ_SCHEDULER_STATE
(
SCHED_NAME VARCHAR(120) NOT NULL comment '定时任务名称',
INSTANCE_NAME VARCHAR(200) NOT NULL comment '实例名称',
LAST_CHECKIN_TIME BIGINT(13) NOT NULL comment '最近检入时间',
CHECKIN_INTERVAL BIGINT(13) NOT NULL comment '检入间隔',
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);
CREATE TABLE QRTZ_LOCKS
(
SCHED_NAME VARCHAR(120) NOT NULL comment '定时任务名称',
LOCK_NAME VARCHAR(40) NOT NULL comment 'lock名称',
PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);
创建两个固定的配置类,这两个类在任何项目中可以使用都是差不多的固定写法
MyJobFactory.java
package com.jiawa.train.batch.config;
import jakarta.annotation.Resource;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import org.springframework.stereotype.Component;
@Component
public class MyJobFactory extends SpringBeanJobFactory {
@Resource
private AutowireCapableBeanFactory beanFactory;
/**
* 这里覆盖了super的createJobInstance方法,对其创建出来的类再进行autowire。
*/
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super.createJobInstance(bundle);
beanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
创建一个config工具类,根据数据库进行调度任务 连接数据库指定定时时间。调度器
package com.jiawa.train.batch.config;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.sql.DataSource;
import java.io.IOException;
@Configuration
public class SchedulerConfig {
@Resource
private MyJobFactory myJobFactory;
@Bean
public SchedulerFactoryBean schedulerFactoryBean(@Qualifier("dataSource") DataSource dataSource) throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);// 连接数据库
factory.setJobFactory(myJobFactory);// 指定前面创建的配置类
factory.setStartupDelay(2); // 延迟2秒启动
return factory;
}
}
接下来开始完成定时任务的接口,使得定时任务让接口来控制,首先创建请求数据request
package com.jiawa.train.batch.req;
public class CronJobReq {
private String group; // 分组
private String name;// 任务名称
private String description;// 描述
private String cronExpression;// 执行时间表达式
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("CronJobDto{");
sb.append("cronExpression='").append(cronExpression).append('\'');
sb.append(", group='").append(group).append('\'');
sb.append(", name='").append(name).append('\'');
sb.append(", description='").append(description).append('\'');
sb.append('}');
return sb.toString();
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
设置响应数据类response,进行set和get
package com.jiawa.train.batch.resp;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.Date;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class CronJobResp {
private String group;// 分组
private String name; // 任务名称
private String description; // 描述
private String state;// 状态
private String cronExpression;// 定时任务表达式
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date nextFireTime;//
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date preFireTime;//
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("CronJobDto{");
sb.append("cronExpression='").append(cronExpression).append('\'');
sb.append(", group='").append(group).append('\'');
sb.append(", name='").append(name).append('\'');
sb.append(", description='").append(description).append('\'');
sb.append(", state='").append(state).append('\'');
sb.append(", nextFireTime=").append(nextFireTime);
sb.append(", preFireTime=").append(preFireTime);
sb.append('}');
return sb.toString();
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getNextFireTime() {
return nextFireTime;
}
public void setNextFireTime(Date nextFireTime) {
this.nextFireTime = nextFireTime;
}
public Date getPreFireTime() {
return preFireTime;
}
public void setPreFireTime(Date preFireTime) {
this.preFireTime = preFireTime;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
创建controller类接口,实现对定时任务的控制返回状态 添加定时任务。 当然如果项目中有一个控台端这就是控台的接口
创建定时任务,暂停,重置,启动定时任务,删除以及查看总共有多少个定时任务打开
package com.jiawa.train.batch.controller;
import com.jiawa.train.batch.req.CronJobReq;
import com.jiawa.train.batch.resp.CronJobResp;
import com.jiawa.train.common.resp.CommonResp;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@RestController
@RequestMapping(value = "/admin/job")
public class JobController {
private static Logger LOG = LoggerFactory.getLogger(JobController.class);
@Autowired
private SchedulerFactoryBean schedulerFactoryBean;
@RequestMapping(value = "/run")
public CommonResp
POST http://localhost:8000/batch/admin/job/add
Content-Type: application/json
{
"name": "com.jiawa.train.batch.job.TestJob",
"jobGroupName": "default",
"cronExpression": "*/2 * * * * ?",
"desc": "test job"
}
###
GET http://localhost:8000/batch/admin/job/query
###
POST http://localhost:8000/batch/admin/job/pause
Content-Type: application/json
{
"name": "com.jiawa.train.batch.job.TestJob",
"jobGroupName": "default"
}
###
POST http://localhost:8000/batch/admin/job/resume
Content-Type: application/json
{
"name": "com.jiawa.train.batch.job.TestJob",
"jobGroupName": "default"
}
###
POST http://localhost:8000/batch/admin/job/reschedule
Content-Type: application/json
{
"name": "com.jiawa.train.batch.job.TestJob",
"jobGroupName": "default",
"cronExpression": "*/5 * * * * ?",
"desc": "test job"
}
###
POST http://localhost:8000/batch/admin/job/delete
Content-Type: application/json
{
"name": "com.jiawa.train.batch.job.TestJob",
"jobGroupName": "default"
}
###
POST http://localhost:8000/batch/admin/job/run
Content-Type: application/json
{
"name": "com.jiawa.train.batch.job.DailyTrainJob",
"jobGroupName": "default"
}
以上就是接口控制定时任务的开始结束添加删除的功能。
下一篇就是多节点场景中如何调度任务quartz