赞
踩
Quartz 是功能强大的开源作业调度库,几乎可以集成到任何 Java 应用程序中,从最小的独立应用程序到最大的电子商务系统。Quartz 可用于创建简单或复杂的计划,以执行数以万计的工作;可以执行您编写的所有内容。
Spring Boot 官方也对 Quartz 调度器进行了集成,Spring boot 官网文档:Quartz Scheduler,Java JDK 也带有 计时器 Timer 以及 定时执行服务 ScheduledExecutorService ,Spring 也提供了 @Scheduled 执行定时任务。
如果实际环境中定时任务过多,处理频繁,建议适应第三方封装的调度框架,因为定时器操作底层都是多线程的操作,任务的启动、暂停、恢复、删除、实质是线程的启动、暂停、中断、唤醒等操作。
Scheduler - 调度器
1、Scheduler 用来对 Trigger 和 Job 进行管理,Trigger 和 JobDetail 可以注册到 Scheduler 中,两者在 Scheduler 中都拥有自己的唯一的组(group)和名称(name)用来进行彼此的区分,Scheduler 可以通过任务组和名称来对 Trigger 和 JobDetail 进行管理。
2、每个 Scheduler 都有一个 SchedulerContext,用来保存 Scheduler 的上下文数据,Job 和 Trigger 都可以获取其中的信息。
3、Scheduler 是由 SchedulerFactory 创建,它有两个实现:DirectSchedulerFactory 、StdSchdulerFactory ,前者可以用来在代码里定制 Schduler 参数,后者直接读取 classpath 下的 quartz.properties(不存在就都使用默认值)配置来实例化 Scheduler。
Job - 任务
1、Job 是一个任务接口,开发者可以实现该接口定义自己的任务,JobExecutionContext 中提供了调度上下文的各种信息。
2、Job 中的任务有可能并发执行,例如任务的执行时间过长,而每次触发的时间间隔太短,则会导致任务会被并发执行。如果是并发执行,就需要一个数据库锁去避免一个数据被多次处理。可以在 execute()方法上添加 @DisallowConcurrentExecution 注解解决这个问题。
JobDetail - 任务详情
1、JobDetail 对象是在将 job 注册到 scheduler 时,由客户端程序创建的,它包含 job 的各种属性设置,以及用于存储 job 实例状态信息的 JobDataMap。
2、JobDetail 由 JobBuilder 创建/定义,Quartz 不存储 Job 的实际实例,但是允许通过使用 JobDetail 定义一个实例。
3、Job 有一个与其关联的名称和组,应该在单个 Scheduler 中唯一标识它们。
4、一个 Trigger(触发器) 只能对应一个 Job(任务),但是一个 Job 可以对应多个 Trigger。JobDetal 与 Trigger 一对多
Trigger - 触发器
1、Trigger 用于触发 Job 的执行。TriggerBuilder 用于定义/构建触发器实例。
2、Trigger也有一个相关联的 JobDataMap,用于给Job传递一些触发相关的参数。
3、Quartz自带了各种不同类型的 Trigger,最常用的主要是SimpleTrigger和CronTrigger。
JobDataMap
1、JobDataMap 实现了 JDK 的 Map 接口,可以以 Key-Value 的形式存储数据。
2、JobDetail、Trigger 实现类中都定义 JobDataMap 成员变量及其 getter、setter 方法,可以用来设置参数信息,Job 执行 execute() 方法的时候,JobExecutionContext 可以获取到 JobDataMap 中的信息。
添加依赖:
- <modelVersion>4.0.0</modelVersion>
-
- <groupId>org.example</groupId>
- <artifactId>quartz-servicer</artifactId>
- <version>1.0.0</version>
-
- <properties>
- <maven.compiler.source>11</maven.compiler.source>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <maven.compiler.target>11</maven.compiler.target>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- <version>2.7.5</version>
- </dependency>
-
- <!-- 公共依赖 -->
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>fastjson</artifactId>
- <version>2.0.19</version>
- </dependency>
-
- <!-- quartz依赖 -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-quartz</artifactId>
- <version>2.5.4</version>
- </dependency>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>8.0.31</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-jdbc</artifactId>
- <version>2.7.3</version>
- </dependency>
-
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>druid-spring-boot-starter</artifactId>
- <version>1.2.14</version>
- <exclusions>
- <exclusion>
- <artifactId>spring-boot-autoconfigure</artifactId>
- <groupId>org.springframework.boot</groupId>
- </exclusion>
- </exclusions>
- </dependency>
-
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
- 复制代码
quartz需要单据的数据库,所以需要单据创建一个库来给quartz使用,我新建了一个scheduler的库,详细信息官方参考quartz-scheduler/quartz
- #1 保存已经触发的触发器状态信息
- DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
- #2 存放暂停掉的触发器表表
- DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
- #3 调度器状态表
- DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
- #4 存储程序的悲观锁的信息(假如使用了悲观锁)
- DROP TABLE IF EXISTS QRTZ_LOCKS;
- #5 简单的触发器表
- DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
- #6 存储两种类型的触发器表
- DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
- #7 定时触发器表
- DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
- #8 以blob 类型存储的触发器
- DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
- #9 触发器表
- DROP TABLE IF EXISTS QRTZ_TRIGGERS;
- #10 job 详细信息表
- DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
- #11 日历信息表
- 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)
- );
-
- #触发器表
- 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)
- );
-
- #简单的触发器表,包括重复次数,间隔,以及已触发的次数
- 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)
- );
-
- #定时触发器表,存储 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(200) 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)
- );
-
- #存储calendarintervaltrigger和dailytimeintervaltrigger两种类型的触发器
- 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)
- );
-
- #以blob 类型存储的触发器
- 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),
- FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
- REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
- );
-
- #日历信息表, quartz可配置一个日历来指定一个时间范围
- 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)
- );
-
- #存放暂停掉的触发器表表
- CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
- (
- SCHED_NAME VARCHAR(120) NOT NULL,
- TRIGGER_GROUP VARCHAR(200) NOT NULL,
- PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
- );
-
- # 存储与已触发的 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)
- );
-
- 3、 调度器状态表
- 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)
- );
- 4、 存储程序的悲观锁的信息(假如使用了悲观锁)
- CREATE TABLE QRTZ_LOCKS
- (
- SCHED_NAME VARCHAR(120) NOT NULL,
- LOCK_NAME VARCHAR(40) NOT NULL,
- PRIMARY KEY (SCHED_NAME,LOCK_NAME)
- );
-
- 复制代码
Quartz 使用一个名为 quartz.properties 的属性文件进行信息配置,必须位于 classpath 下,是 StdSchedulerFactory 用于创建 Scheduler 的默认属性文件。默认情况下 StdSchedulerFactory 从类路径下加载名为 “quartz.properties” 的属性文件,如果失败,则加载 org/quartz 包中的“quartz.properties”文件,因为我需要的是新建一个Scheduler服务,所以直接使用application.yml,配置如下:
- datasource:
- url: jdbc:mysql://127.0.0.1:3306/scheduler
- username: ***
- password: ****
- driver-class-name: com.mysql.cj.jdbc.Driver
- type: com.alibaba.druid.pool.DruidDataSource
- #定时配置
- quartz:
- #相关属性配置
- properties:
- org:
- quartz:
- scheduler:
- instanceName: local-scheduler-svc
- instanceId: AUTO
- jobStore:
- #表示 quartz 中的所有数据,比如作业和触发器的信息都保存在内存中(而不是数据库中)
- class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
- # 驱动配置
- driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
- # 表前缀
- tablePrefix: QRTZ_
- #是否为集群
- isClustered: false
- clusterCheckinInterval: 10000
- useProperties: false
- dataSource: quartzDs
- #线程池配置
- threadPool:
- class: org.quartz.simpl.SimpleThreadPool
- #线程数
- threadCount: 10
- #优先级
- threadPriority: 5
- #线程继承上下文类加载器的初始化线程
- threadsInheritContextClassLoaderOfInitializingThread: true
- #数据库方式
- job-store-type: JDBC
- #初始化表结构
- jdbc:
- initialize-schema: NEVER
-
- 复制代码
jobName:任务名称
jobGroup:任务组
jsonParams:任务执行信息(在用户定时远程调用接口的时候,我们可以接口信息封装到这个Map中)
cron:定时任务的cron表达式
timeZoneId:定制执行任务的时区
triggerTime:定时器时间(目前没用上)
- @Data
- public class JobInfo {
-
- private String jobName;
-
- private String jobGroup;
-
- private Map<String, Object> jsonParams;
-
- private String cron;
-
- private String timeZoneId;
-
- private Date triggerTime;
- }
- 复制代码
execute()里面就是任务的逻辑:
① 使用HttpURLConnection
发送网络请求,利用BufferedReader接收请求返回的结果
② 在任务的Description中取出定时请求的接口信息,解析Description,获取请求url
③ 编辑请求信息,通过URL类编辑请求信息,使用HttpURLConnection发送请求,并接收请求返回的状态码,根据状态码,判断是否请求成功,请求成功便通过BufferedReader读取响应信息,返回请求结果
- import com.alibaba.fastjson.JSONObject;
- import org.quartz.DisallowConcurrentExecution;
- import org.quartz.Job;
- import org.quartz.JobExecutionContext;
- import org.quartz.JobExecutionException;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.util.StringUtils;
-
- import java.io.BufferedReader;
- import java.io.InputStreamReader;
- import java.net.HttpURLConnection;
- import java.net.URL;
- import java.util.Objects;
-
- @DisallowConcurrentExecution
- public class HttpRemoteJob implements Job {
- //日志
- private static final Logger log = LoggerFactory.getLogger(HttpRemoteJob.class);
-
- @Override
- public void execute(JobExecutionContext context)throws JobExecutionException {
- //用于发送网络请求
- HttpURLConnection connection = null;
- //用于接收请求返回的结果
- BufferedReader bufferedReader = null;
- //获取任务Description述,之前我们把接口请求的信息放在Description里面了
- String jsonParams = context.getJobDetail().getDescription();
- if (StringUtils.isEmpty(jsonParams)){
- return;
- }
- //解析Description,获取请求url
- JSONObject jsonObj= (JSONObject) JSONObject.parse(jsonParams);
- String callUrl = jsonObj.getString("callUrl");
- if(StringUtils.isEmpty(callUrl)) {
- return;
- }
-
- try {
- //编辑请求信息
- URL realUrl = new URL(callUrl);
- connection = (HttpURLConnection) realUrl.openConnection();
-
- connection.setRequestMethod("GET");
- connection.setDoOutput(true);
- connection.setDoInput(true);
- connection.setUseCaches(false);
- connection.setReadTimeout(5 * 1000);
- connection.setConnectTimeout(3 * 1000);
- connection.setRequestProperty("connection", "Keep-Alive");
- connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
- connection.setRequestProperty("Accept-Charset", "application/json;charset=UTF-8");
- //发送请求
- connection.connect();
-
- //获取请求返回的状态吗
- int statusCode = connection.getResponseCode();
- if (statusCode != 200){
- //请求失败抛出异常
- throw new RuntimeException("Http Request StatusCode(" + statusCode + ") Invalid.");
- }
- //如果返回值正常,数据在网络中是以流的形式得到服务端返回的数据
- bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
- StringBuilder stringBuilder = new StringBuilder();
- String line;
- // 从流中读取响应信息
- while ((line = bufferedReader.readLine()) != null) {
- stringBuilder.append(line);
- }
- String responseMsg = stringBuilder.toString();
- log.info(responseMsg);
-
- } catch (Exception e) {
- log.error(e.getMessage());
- } finally {
- //关闭流与请求连接
- try {
- if (Objects.nonNull(bufferedReader)){
- bufferedReader.close();
- }
- if (Objects.nonNull(connection)){
- connection.disconnect();
- }
- } catch (Exception e) {
- log.error(e.getMessage());
- }
- }
- }
-
- }
- 复制代码
@DisallowConcurrentExecution
的作用:
Quartz定时任务默认是并发执行的,不会等待上一次任务执行完毕,只要有间隔时间到就会执行, 如果定时任执行太长,会长时间占用资源,导致其它任务堵塞。
@DisallowConcurrentExecution
这个注解是加在Job类上的,是禁止并发执行多个相同定义的JobDetail, , 但并不是不能同时执行多个Job, 而是不能并发执行同一个Job Definition(由JobDetail定义), 但是可以同时执行多个不同的JobDetail。
JobExecutionContext
类可以获取很多任务的信息:
- @Override
- public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
- JobKey jobKey = jobExecutionContext.getJobDetail().getKey();
- //工作任务名称
- String jobName = jobKey.getName();
- //工作任务组名称
- String groupName = jobKey.getGroup();
- //任务类名称(带路径)
- String classPathName = jobExecutionContext.getJobDetail().getJobClass().getName();
- //任务类名称
- String className = jobExecutionContext.getJobDetail().getJobClass().getSimpleName();
- //获取Trigger内容
- TriggerKey triggerKey = jobExecutionContext.getTrigger().getKey();
- //触发器名称
- String triggerName = triggerKey.getName();
- //出发组名称(带路径)
- String triggerPathName = jobExecutionContext.getTrigger().getClass().getName();
- //触发器类名称
- String triggerClassName = jobExecutionContext.getTrigger().getClass().getSimpleName();
- }
-
- 复制代码
注意:
- //解析Description,获取请求url
- JSONObject jsonObj= (JSONObject) JSONObject.parse(jsonParams);
- String callUrl = jsonObj.getString("callUrl");
- 复制代码
之前我们封装JobInfo信息是将Map<String, Object> jsonParams保存接口请求信息,们去解析接口请求的时候也要用callUrl
去取,那么在封装JobInfo类时,Map中就需要指定key是callUrl
,不然取不到就会报错了。
JobServiceImpl业务逻辑:
JobInfo携带这我们需要新建任务的信息
① 通过jobName和jobGroup可以查询到任务唯一的jobKey,通过getJobDetail(jobKey),判断是否已有这个任务,有的话先删除在新增这个任务
② 新建一个任务JobDetail,withDescription属性中是指任务描述,JobInfo类中JsonParams属性是一个Map,这里需要将Map格式化一下,不然无法赋给withDescription,这个JsonUtils
在下面:
- //任务详情
- JobDetail jobDetail = JobBuilder.newJob(HttpRemoteJob.class)
- .withDescription(JsonUtils.object2Json(jobInfo.getJsonParams())) //任务描述
- .withIdentity(jobKey) //指定任务
- .build();
- 复制代码
③ 创建触发器,触发器有多种类型,需要定时执行就使用cron表达式,创建cron调度器建造器CronScheduleBuilder
,再创建Trigger触发器,调度器建造器有很多种,除了我下面用到的简单调度器构造器SimpleTrigger
,还有:
CalendarIntervalScheduleBuilder
: 每隔一段时间执行一次(年月日)
DailyTimeIntervalScheduleBuilder
: 设置年月日中的某些固定日期,可以设置执行总次数
以后我们再单独写一片介绍;
CronScheduleBuilder和SimpleTrigger的区别在于:CronScheduleBuilder是通过cron表达式定时某个时间点或多个时间点定时直接,而SimpleTrigger是周期性执行,着重与时间间隔,着重与周期执行;
④ 将任务添加到Scheduler中
此外还有一些Scheduler的其他方法:
获取任务触发器 :TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
停止触发器 :scheduler.pauseTrigger(triggerKey);
移除触发器:scheduler.unscheduleJob(triggerKey);
删除任务:scheduler.deleteJob(JobKey.jobKey(jobName,jobGroup));
根据jobName,jobGroup获取jobKey 恢复任务:scheduler.resumeJob(JobKey.jobKey(jobName,jobGroup));
根据jobName,jobGroup获取jobKey 暂停任务: scheduler.pauseJob(JobKey.jobKey(jobName,jobGroup));
根据jobName,jobGroup获取jobKey 立即执行任务: scheduler.triggerJob(JobKey.jobKey(jobName,jobGroup));
在下面的实现类代码中有很好的用例;
JsonUtils工具类
- public class JsonUtils {
-
- public static final ObjectMapper OBJECT_MAPPER = createObjectMapper();
-
- private static final ObjectMapper IGNORE_OBJECT_MAPPER = createIgnoreObjectMapper();
-
- private static ObjectMapper createIgnoreObjectMapper() {
- ObjectMapper objectMapper = createObjectMapper();
- objectMapper.addMixIn(Object.class, DynamicMixIn.class);
- return objectMapper;
- }
-
- public static ObjectMapper createObjectMapper() {
- ObjectMapper objectMapper = new ObjectMapper();
- objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
- objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
- objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
- objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
- objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
- objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
- objectMapper.registerModule(new JavaTimeModule());
- return objectMapper;
- }
-
- public static String object2Json(Object o) {
- StringWriter sw = new StringWriter();
- JsonGenerator gen = null;
- try {
- gen = new JsonFactory().createGenerator(sw);
- OBJECT_MAPPER.writeValue(gen, o);
- } catch (IOException e) {
- throw new RuntimeException("Cannot serialize object as JSON", e);
- } finally {
- if (null != gen) {
- try {
- gen.close();
- } catch (IOException e) {
- throw new RuntimeException("Cannot serialize object as JSON", e);
- }
- }
- }
- return sw.toString();
- }
- }
- 复制代码
JobService接口
- import liu.qingxu.domain.JobInfo;
- import org.springframework.web.bind.annotation.RequestBody;
-
- /**
- * @module
- * @author: qingxu.liu
- * @date: 2022-11-15 14:37
- * @copyright
- **/
- public interface JobService {
-
- /**
- * 新建一个定时任务
- * @param jobInfo 任务信息
- * @return 任务信息
- */
- JobInfo save(@RequestBody JobInfo jobInfo);
-
- /**
- * 新建一个简单定时任务
- * @param jobInfo 任务信息
- * @return 任务信息
- */
- JobInfo simpleSave(@RequestBody JobInfo jobInfo);
-
- /**
- * 删除任务
- * @param jobName 任务名称
- * @param jobGroup 任务组
- */
- void remove( String jobName,String jobGroup);
-
- /**
- * 恢复任务
- * @param jobName 任务名称
- * @param jobGroup 任务组
- */
- void resume(String jobName, String jobGroup);
-
- /**
- * 暂停任务
- * @param jobName 任务名称
- * @param jobGroup 任务组
- */
- void pause(String jobName, String jobGroup);
-
- /**
- * 立即执行任务一主要是用于执行一次任务的场景
- * @param jobName 任务名称
- * @param jobGroup 任务组
- */
- void trigger(String jobName, String jobGroup);
- }
-
- 复制代码
JobServiceImpl实现类
- import liu.qingxu.domain.JobInfo;
- import liu.qingxu.executors.HttpRemoteJob;
- import liu.qingxu.service.JobService;
- import liu.qingxu.utils.JsonUtils;
- import org.quartz.CronScheduleBuilder;
- import org.quartz.JobBuilder;
- import org.quartz.JobDetail;
- import org.quartz.JobKey;
- import org.quartz.Scheduler;
- import org.quartz.SchedulerException;
- import org.quartz.SimpleTrigger;
- import org.quartz.Trigger;
- import org.quartz.TriggerBuilder;
- import org.quartz.TriggerKey;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
-
- import java.util.Objects;
- import java.util.TimeZone;
-
- /**
- * @module
- * @author: qingxu.liu
- * @date: 2022-11-15 14:48
- * @copyright
- **/
- @Service
- public class JobServiceImpl implements JobService {
-
- @Autowired
- private Scheduler scheduler;
-
- @Override
- public JobInfo save(JobInfo jobInfo) {
- //查询是否已有相同任务 jobKey可以唯一确定一个任务
- JobKey jobKey = JobKey.jobKey(jobInfo.getJobName(), jobInfo.getJobGroup());
- try {
- JobDetail jobDetail = scheduler.getJobDetail(jobKey);
- if (Objects.nonNull(jobDetail)){
- scheduler.deleteJob(jobKey);
- }
- } catch (SchedulerException e) {
- e.printStackTrace();
- }
-
- //任务详情
- JobDetail jobDetail = JobBuilder.newJob(HttpRemoteJob.class)
- .withDescription(JsonUtils.object2Json(jobInfo.getJsonParams())) //任务描述
- .withIdentity(jobKey) //指定任务
- .build();
- //根据cron,TimeZone时区,指定执行计划
- CronScheduleBuilder builder = CronScheduleBuilder.cronSchedule(jobInfo.getCron())
- .inTimeZone(TimeZone.getTimeZone(jobInfo.getTimeZoneId()));
-
- //触发器
- Trigger trigger = TriggerBuilder.newTrigger()
- .withIdentity(jobInfo.getJobName(), jobInfo.getJobGroup()).startNow()
- .withSchedule(builder)
- .build();
-
- //添加任务
- try {
- scheduler.scheduleJob(jobDetail, trigger);
- } catch (SchedulerException e) {
- System.out.println(e.getMessage());
- }
- return jobInfo;
- }
-
- @Override
- public JobInfo simpleSave(JobInfo jobInfo) {
- JobKey jobKey = JobKey.jobKey(jobInfo.getJobName(), jobInfo.getJobGroup()); //作业名称及其组名
- //判断是否有相同的作业
- try {
- JobDetail jobDetail = scheduler.getJobDetail(jobKey);
- if(jobDetail != null){
- scheduler.deleteJob(jobKey);
- }
- } catch (SchedulerException e) {
- e.printStackTrace();
- }
- //定义作业的详细信息,并设置要执行的作业类名,设置作业名称及其组名
- JobDetail jobDetail = JobBuilder.newJob(HttpRemoteJob.class)
- .withDescription(JsonUtils.object2Json(jobInfo.getJsonParams()))
- .withIdentity(jobKey)
- .build()
- ;
- //简单触发器,着重与时间间隔
- SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
- .withIdentity(jobInfo.getJobName(), jobInfo.getJobGroup())
- .startAt(jobInfo.getTriggerTime())
- .build();
-
- try {
- scheduler.scheduleJob(jobDetail, trigger);
- } catch (SchedulerException e) {
- System.out.println(e.getMessage());
- }
-
- return jobInfo;
- }
-
- @Override
- public void remove(String jobName, String jobGroup) {
- //获取任务触发器
- TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
- try {
- //停止触发器
- scheduler.pauseTrigger(triggerKey);
- //移除触发器
- scheduler.unscheduleJob(triggerKey);
- //删除任务
- scheduler.deleteJob(JobKey.jobKey(jobName,jobGroup));
- } catch (SchedulerException e) {
- System.out.println(e.getMessage());
- }
- }
-
- @Override
- public void resume(String jobName, String jobGroup) {
- try {
- //根据jobName,jobGroup获取jobKey 恢复任务
- scheduler.resumeJob(JobKey.jobKey(jobName,jobGroup));
- } catch (SchedulerException e) {
- System.out.println(e.getMessage());
- }
- }
-
- @Override
- public void pause(String jobName, String jobGroup) {
- try {
- //根据jobName,jobGroup获取jobKey 暂停任务
- scheduler.pauseJob(JobKey.jobKey(jobName,jobGroup));
- } catch (SchedulerException e) {
- System.out.println(e.getMessage());
- }
- }
-
- @Override
- public void trigger(String jobName, String jobGroup) {
- try {
- //根据jobName,jobGroup获取jobKey 立即执行任务
- scheduler.triggerJob(JobKey.jobKey(jobName,jobGroup));
- } catch (SchedulerException e) {
- System.out.println(e.getMessage());
- }
- }
- }
- 复制代码
到此为止,我的定时调用接口任务已经完成了,现在我们写个Controller来试着调用一下:
我们调用test接口,新建一个任务,让任务每隔5秒调用runTest接口,然后在调用deleteTest接口删除任务;
- @RestController
- @RequestMapping("/quartz/job")
- public class QuartzController {
-
- private final JobService jobService;
- public QuartzController(JobService jobService) {
- this.jobService = jobService;
- }
-
- @GetMapping("/test")
- public void test(){
- JobInfo jobInfo = new JobInfo();
- jobInfo.setJobName("test-job");
- jobInfo.setJobGroup("test");
- jobInfo.setTimeZoneId("Asia/Shanghai"); //时区指定上海
- jobInfo.setCron("0/5 * * * * ? "); //每5秒执行一次
- Map<String, Object> params = new HashMap<>();
- //添加需要调用的接口信息
- String callUrl = "http://127.0.0.1:8080/quartz/job/test/run";
- params.put("callUrl", callUrl);
- jobInfo.setJsonParams(params);
- jobService.save(jobInfo);
- }
-
-
- @GetMapping("/test/run")
- public void runTest(){
- System.out.println(new Date());
- }
-
- @GetMapping("/test/delete")
- public void deleteTest(){
- jobService.remove("test-job","test");
- System.out.println("任务已删除");
- }
- }
- 复制代码
可以看到runTest接口每隔5秒就被任务调用了一个,这时候我们去看数据库中的scheduler库的QRTZ_JOB_DETAILS表和QRTZ_TRIGGERS表,可以看到我们我们定时任务的信息,表示我们的定时任务持久化成功,这个时候你关掉服务器,再重启任务也会定时去执行runTest接口;
然后我们再调用deleteTest接口删除任务
此时再看去看数据库中的scheduler库的QRTZ_JOB_DETAILS表和QRTZ_TRIGGERS表中就没有之前的任务数据了
end~~~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。