【微服务】springboot整合quartz使用详解

目录

一、前言

二、quartz介绍

2.1 quartz概述

2.2 quartz优缺点

2.3 quartz核心概念

2.3.1 Scheduler

2.3.2 Trigger

2.3.3 Job

2.3.4 JobDetail

2.4 Quartz作业存储类型

2.5 适用场景

三、Cron表达式

3.1 Cron表达式语法

3.2 Cron表达式各元素说明

3.3 Cron表达式字符说明

四、环境准备

4.1 搭建步骤

4.1.1 拉取镜像

4.1.2 创建相关的数据目录

4.1.3 启动容器

五、springboot整合quartz

5.1 导入maven依赖

5.2 初始化数据表

5.3 增加配置文件

5.4 核心业务类

5.4.1 前端控制器类

5.4.2 业务实现类

5.4.3 mybatis-plus配置类

5.4.4 job执行任务类

5.5 效果测试

5.5.1 添加任务

5.5.2 暂停任务

5.6 补充说明

六、写在结尾


一、前言

在项目开发中,经常需要定时任务来处理一些业务,可以说定时任务的场景无处不在,比如定时同步数据,定时清理数据等,在使用SpringBoot框架中,简单的场景,可以考虑使用内置的注解就可以使用,但是如果需要调度的场景比较复杂,或者说需要更灵活的配置调度,就需要考虑采用其他的方案了,比如Quartz , xxl-job,elastic job等。

二、quartz介绍

2.1 quartz概述

Quartz 是 OpenSymphony 开源组织在 Job Scheduling 领域又一个开源项目,是完全由 Java 开发的一个开源任务日程管理系统,“任务进度管理器”就是一个在预先确定(被纳入日程)的时间到达时,负责执行(或者通知)其他软件组件的系统。Quartz 是一个开源的作业调度框架,它完全由 Java 写成,并设计用于 J2SE 和 J2EE 应用中,它提供了巨大的灵活性而不牺牲简单性。

官网地址:Quartz Enterprise Job Scheduler

2.2 quartz优缺点

quartz具备如下优点:

  • 丰富的 Job 操作 API;
  • 支持多种配置;
  • SpringBoot 无缝集成;
  • 支持数据持久化;
  • 支持集群部署;

Quartz 还支持开源,是一个功能丰富的开源作业调度库,可以集成到几乎任何 Java 应用程序中;

缺点:

  • 学习成本:由于Quartz是一个相对复杂的框架,使用它需要一定的学习成本。开发人员需要熟悉其概念、配置和API,以正确地使用和管理任务调度;
  • 配置复杂性:Quartz的配置文件较为复杂,特别是在涉及到分布式环境或复杂任务依赖关系时。正确配置和管理Quartz调度器可能需要一定的技术知识和经验‘

 

2.3 quartz核心概念

在真正开始学习quartz之前,有必要对quartz中的几个核心业务概念做全面的了解。下面几个核心组件的关系如下:

2.3.1 Scheduler

Quartz 中的任务调度器,通过 Trigger 和 JobDetail 可以用来调度、暂停和删除任务,提供调度任务的主要API;

调度器就相当于一个容器,装载着任务和触发器,该类是一个接口,代表一个 Quartz 的独立运行容器,Trigger 和 JobDetail 可以注册到 Scheduler 中,两者在 Scheduler 中拥有各自的组及名称,组及名称是 Scheduler 查找定位容器中某一对象的依据,Trigger 的组及名称必须唯一,JobDetail 的组和名称也必须唯一(但可以和 Trigger 的组和名称相同,因为它们是不同类型的);

2.3.2 Trigger

定义调度执行计划的组件,即什么时候触发执行,Trigger是Quartz 中的触发器,是一个类,描述触发 Job 执行的时间触发规则,主要有 SimpleTrigger 和 CronTrigger 这两个子类。

1)当且仅当需调度一次或者以固定时间间隔周期执行调度,SimpleTrigger 是最适合的选择;

2)CronTrigger 则可以通过 Cron 表达式定义出各种复杂时间规则的调度方案:如工作日周一到周五的 16:00 ~16:10 执行调度等;

2.3.3 Job

Quartz 中具体的任务,包含了执行任务的具体方法,是一个接口,只定义一个方法 execute() 方法,在实现接口的 execute() 方法中编写所需要定时执行的 Job。

2.3.4 JobDetail

Quartz 中需要执行的任务详情,包括了任务的唯一标识和具体要执行的任务,可以通过 JobDataMap 往任务中传递数据。

2.4 Quartz作业存储类型

RAMJobStore

RAM 也就是内存,默认情况下 Quartz 会将任务调度存储在内存中,这种方式性能是最好的,因为内存的速度是最快的。不好的地方就是数据缺乏持久性,但程序崩溃或者重新发布的时候,所有运行信息都会丢失

JDBC 作业存储

存到数据库之后,可以做单点也可以做集群,当任务多了之后,可以统一进行管理,随时停止、暂停、修改任务。关闭或者重启服务器,运行的信息都不会丢失。缺点就是运行速度快慢取决于连接数据库的快慢

2.5 适用场景

在下面的场景中你可以考虑适用quartz

定时任务

Quartz最常见的应用场景是执行定时任务,比如每天凌晨生成报表、每小时执行数据备份等。它可以根据配置的时间表触发任务,并在预定的时间间隔内重复执行。

计划任务

除了定时任务,Quartz还支持基于日历的计划任务。例如,在特定的日期或周几执行某个任务,或者排除特定的日期和时间段。

分布式任务调度

Quartz可以与分布式系统集成,实现分布式任务调度。它提供了可靠的任务调度机制,能够确保在分布式环境中准确地调度和执行任务。

监控和管理

Quartz提供了监控和管理任务的功能。它可以获取任务的执行状态、日志和性能指标,并提供了对任务的管理接口,如启动、停止、暂停和恢复任务等。

三、Cron表达式

Cron 表达式是任务调度的关键要素,简单来说,Cron 表达式是一个字符串,包括6~7个时间元素,每个元素都有特定的含义,在 Quartz中可以用于指定任务的执行时间。比如:0/10 * * * * ?

3.1 Cron表达式语法

cron表达式中各个位置的元素代表的含义如下

SecondsMinutesHoursDayofMonthMonthDayofWeek
分钟小时日期天/日日期月份星期

3.2 Cron表达式各元素说明

cron表达式中各个位置的元素的用法参考如下

时间元素可出现的字符有效数值范围
Seconds, - * /0-59
Minutes, - * /0-59
Hours, - * /0-23
DayofMonth, - * / ? L W0-31
Month, - * /1-12
DayofWeek, - * / ? L #1-7或SUN-SAT

3.3 Cron表达式字符说明

字符作用举例
,列出枚举值在Minutes域使用5,10,表示在5分和10分各触发一次
-表示触发范围在Minutes域使用5-10,表示从5分到10分钟每分钟触发一次
*匹配任意值在Minutes域使用*, 表示每分钟都会触发一次
/

起始时间开始触发,每隔固定时间触发

一次

在Minutes域使用5/10,表示5分时触发一次,每10分钟再触发一次
?

在DayofMonth和DayofWeek中,用于匹

配任意值

在DayofMonth域使用?,表示每天都触发一次
#在DayofMonth中,确定第几个星期几1#3表示第三个星期日
L表示最后在DayofWeek中使用5L,表示在最后一个星期四触发
W表示有效工作日(周一到周五)在DayofMonth使用5W,如果5日是星期六,则将在最近的工作日4日触发一次

在线 Cron 表达式生成器,其实 Cron 表达式无需多记,需要使用的时候直接使用在线生成器就可以了

地址1: 在线Cron表达式生成器

地址2: 在线Cron表达式生成器

四、环境准备

在实际使用quartz时,任务调度相关的配置数据肯定是要入库的,即Quartz作业存储类型使用jdbc作业存储,所以需要准备mysql环境,下面使用docker快速搭建mysql环境。

4.1 搭建步骤

4.1.1 拉取镜像

docker pull mysql:5.7

4.1.2 创建相关的数据目录

mkdir -p /usr/local/mysql/datamkdir -p /usr/local/mysql/logsmkdir -p /usr/local/mysql/conf

4.1.3 启动容器

docker run --name mysql -p 3306:3306 \
-v /usr/local/mysql/data:/var/lib/mysql \
-v /usr/local/mysql/logs:/logs \
-v /usr/local/mysql/conf:/etc/mysql/conf.d \-e MYSQL_ROOT_PASSWORD=密码 -d mysql:5.7

五、springboot整合quartz

网上关于quartz与springboot的整合方式有很多,但很多样例无法跑起来,请参选接下来的操作步骤,完成一个完整的整合。完整的工程目录如下:

5.1 导入maven依赖

以下为本例必须的依赖,可以根据自身的情况酌情添加

    <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.2</version><relativePath/></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.1.4</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version></dependency></dependencies>

5.2 初始化数据表

上面导入完成依赖jar包之后,在依赖的jar包中,找到与调度任务初始化的一个sql

拷贝上面的sql文件中的初始化sql,在mysql中创建一个数据库,这里命名为:quartz_db,然后执行即可,执行成功后,可以看到与任务调度相关的数据表就初始化了

在此基础上,再额外增加一张表,与我们可能有业务关联的信息整合

DROP TABLE IF EXISTS `sys_quartz_job`;CREATE TABLE `sys_quartz_job`  (`id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`create_by` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人',`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',`del_flag` int(1) NULL DEFAULT NULL COMMENT '删除状态',`update_by` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '修改人',`update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',`job_class_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '任务类名',`cron_expression` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'cron表达式',`parameter` varchar(255) 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 '描述',`status` int(1) NULL DEFAULT NULL COMMENT '状态 0正常 -1停止',PRIMARY KEY (`id`) USING BTREE) ENGINE = MyISAM CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;

5.3 增加配置文件

配置application.yml文件

server:port: 8088
spring:## quartz定时任务,采用数据库方式quartz:job-store-type: jdbc#json 时间戳统一转换jackson:date-format: yyyy-MM-dd HH:mm:sstime-zone: GMT+8datasource:url: jdbc:mysql://IP:3306/quartz_db?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&useSSL=falseusername: rootpassword: 密码driver-class-name: com.mysql.jdbc.Driverjta:atomikos:properties:recovery:forget-orphaned-log-entries-delay:
#mybatis plus 设置
mybatis-plus:mapper-locations: classpath:resources/mapper/*global-config:# 关闭MP3.0自带的bannerbanner: falsedb-config:#主键类型  0:"数据库ID自增",1:"该类型为未设置主键类型", 2:"用户输入ID",3:"全局唯一ID (数字类型唯一ID)", 4:"全局唯一ID UUID",5:"字符串全局唯一ID (idWorker 的字符串表示)";table-underline: truelogic-delete-value: 1 # 逻辑已删除值(默认为 1)logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)configuration:# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用#log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 返回类型为Map,显示null对应的字段call-setters-on-nulls: truelog-impl:   org.apache.ibatis.logging.stdout.StdOutImpl

5.4 核心业务类

下面列举出工程中核心的类文件,编写的顺序为从上至下,即从接口开始

5.4.1 前端控制器类

该类主要提供与交互操作相关的API接口,后续可以通过界面的配置方式操作任务

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.congge.common.CommonConstant;
import com.congge.common.Result;
import com.congge.entity.QuartzJob;
import com.congge.exception.BizException;
import com.congge.service.QuartzJobService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;import java.util.Arrays;
import java.util.List;@Slf4j
@RestController
@RequestMapping("/job")
public class QuartzController {@Autowiredprivate QuartzJobService quartzJobService;@Autowiredprivate Scheduler scheduler;/*** 分页列表查询** @return*/@RequestMapping(value = "/list", method = RequestMethod.GET)public Result<?> queryPageList(QuartzJob quartzJob, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {Page<QuartzJob> page = new Page<QuartzJob>(pageNo, pageSize);IPage<QuartzJob> pageList = quartzJobService.page(page);return Result.ok(pageList);}/*** 添加定时任务** @param quartzJob* @return*/@PostMapping("/add")public Result<?> add(@RequestBody QuartzJob quartzJob) {List<QuartzJob> list = quartzJobService.list(new QueryWrapper<QuartzJob>().eq("job_class_name", quartzJob.getJobClassName()));if (list != null && list.size() > 0) {return Result.error("该定时任务类名已存在");}quartzJobService.saveAndScheduleJob(quartzJob);return Result.ok("创建定时任务成功");}/*** 更新定时任务** @param quartzJob* @return*/@PostMapping("/edit")public Result<?> eidt(@RequestBody QuartzJob quartzJob) {try {quartzJobService.editAndScheduleJob(quartzJob);} catch (SchedulerException e) {log.error(e.getMessage(), e);return Result.error("更新定时任务失败!");}return Result.ok("更新定时任务成功!");}/*** 通过id删除** @param id* @return*/@GetMapping("/delete")public Result<?> delete(@RequestParam(name = "id", required = true) String id) {QuartzJob quartzJob = quartzJobService.getById(id);if (quartzJob == null) {return Result.error("未找到对应实体");}quartzJobService.deleteAndStopJob(quartzJob);return Result.ok("删除成功!");}/*** 批量删除** @param ids* @return*/@RequestMapping(value = "/deleteBatch", method = RequestMethod.DELETE)public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {if (ids == null || "".equals(ids.trim())) {return Result.error("参数不识别!");}for (String id : Arrays.asList(ids.split(","))) {QuartzJob job = quartzJobService.getById(id);quartzJobService.deleteAndStopJob(job);}return Result.ok("删除定时任务成功!");}/*** 暂停定时任务** @param jobClassName* @return*/@GetMapping(value = "/pause")public Result<Object> pauseJob(@RequestParam(name = "jobClassName", required = true) String jobClassName) {QuartzJob job = null;try {job = quartzJobService.getOne(new LambdaQueryWrapper<QuartzJob>().eq(QuartzJob::getJobClassName, jobClassName));if (job == null) {return Result.error("定时任务不存在!");}scheduler.pauseJob(JobKey.jobKey(jobClassName.trim()));} catch (SchedulerException e) {throw new BizException("暂停定时任务失败");}job.setStatus(CommonConstant.STATUS_DISABLE);quartzJobService.updateById(job);return Result.ok("暂停定时任务成功");}/*** 恢复定时任务** @param jobClassName* @return*/@GetMapping(value = "/resume")public Result<Object> resumeJob(@RequestParam(name = "jobClassName", required = true) String jobClassName) {QuartzJob job = quartzJobService.getOne(new LambdaQueryWrapper<QuartzJob>().eq(QuartzJob::getJobClassName, jobClassName));if (job == null) {return Result.error("定时任务不存在!");}quartzJobService.resumeJob(job);//scheduler.resumeJob(JobKey.jobKey(job.getJobClassName().trim()));return Result.ok("恢复定时任务成功");}/*** 通过id查询** @param id* @return*/@GetMapping("/queryById")public Result<?> queryById(@RequestParam(name = "id", required = true) String id) {QuartzJob quartzJob = quartzJobService.getById(id);return Result.ok(quartzJob);}
}

5.4.2 业务实现类

接口控制器的业务逻辑实现

import com.congge.common.CommonConstant;
import com.congge.entity.QuartzJob;
import com.congge.exception.BizException;
import com.congge.mapper.QuartzJobMapper;
import com.congge.service.QuartzJobService;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import lombok.extern.slf4j.Slf4j;@Slf4j
@Service
public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob> implements QuartzJobService {//    @Autowired
//    private QuartzJobMapper quartzJobMapper;@Autowiredprivate Scheduler scheduler;/*** 保存&启动定时任务*/@Overridepublic boolean saveAndScheduleJob(QuartzJob quartzJob) {if (CommonConstant.STATUS_NORMAL.equals(quartzJob.getStatus())) {// 定时器添加this.schedulerAdd(quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());}// DB设置修改return this.save(quartzJob);}/*** 恢复定时任务*/@Overridepublic boolean resumeJob(QuartzJob quartzJob) {schedulerDelete(quartzJob.getJobClassName().trim());schedulerAdd(quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());quartzJob.setStatus(CommonConstant.STATUS_NORMAL);return this.updateById(quartzJob);}@Overridepublic void test(String param) {System.out.println("param====>"+param);}/*** 编辑&启停定时任务* @throws SchedulerException*/@Overridepublic boolean editAndScheduleJob(QuartzJob quartzJob) throws SchedulerException {if (CommonConstant.STATUS_NORMAL.equals(quartzJob.getStatus())) {schedulerDelete(quartzJob.getJobClassName().trim());schedulerAdd(quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());}else{scheduler.pauseJob(JobKey.jobKey(quartzJob.getJobClassName().trim()));}return this.updateById(quartzJob);}/*** 删除&停止删除定时任务*/@Overridepublic boolean deleteAndStopJob(QuartzJob job) {schedulerDelete(job.getJobClassName().trim());return this.removeById(job.getId());}/*** 添加定时任务** @param jobClassName* @param cronExpression* @param parameter*/private void schedulerAdd(String jobClassName, String cronExpression, String parameter) {try {// 启动调度器scheduler.start();// 构建job信息JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(jobClassName).usingJobData("parameter", parameter).build();// 表达式调度构建器(即任务执行的时间)CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);// 按新的cronExpression表达式构建一个新的triggerCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobClassName).withSchedule(scheduleBuilder).build();scheduler.scheduleJob(jobDetail, trigger);} catch (SchedulerException e) {throw new BizException("创建定时任务失败", e);} catch (RuntimeException e) {throw new BizException(e.getMessage(), e);}catch (Exception e) {throw new BizException("后台找不到该类名:" + jobClassName, e);}}/*** 删除定时任务** @param jobClassName*/private void schedulerDelete(String jobClassName) {try {/*使用给定的键暂停Trigger 。*/scheduler.pauseTrigger(TriggerKey.triggerKey(jobClassName));/*从调度程序中删除指示的Trigger */scheduler.unscheduleJob(TriggerKey.triggerKey(jobClassName));/*从 Scheduler 中删除已识别的Job - 以及任何关联的Trigger */scheduler.deleteJob(JobKey.jobKey(jobClassName));} catch (Exception e) {log.error(e.getMessage(), e);throw new BizException("删除定时任务失败");}}private static Job getClass(String classname) throws Exception {Class<?> class1 = Class.forName(classname);return (Job) class1.newInstance();}}

5.4.3 mybatis-plus配置类

MybatisPlusConfig,配置分页信息

@Configuration
public class MybatisPlusConfig {/*** 分页*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));// 注册乐观锁 插件return mybatisPlusInterceptor;}}

MyMetaObjectHandler,统一处理时间日期等字段信息

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {this.setFieldValByName("createTime",new Date(),metaObject);this.setFieldValByName("updateTime",new Date(),metaObject);}@Overridepublic void updateFill(MetaObject metaObject) {this.setFieldValByName("updateTime",new Date(),metaObject);}
}

5.4.4 job执行任务类

在实际开发中,具体执行各类业务时,需要根据需求场景编写任务执行类,下面提供两个测试使用的任务类

不带参数的任务

@Slf4j
public class SampleJob implements Job {@Overridepublic void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {log.info(String.format("执行普通定时任务 SampleJob !  当前时间:" + new Date()));}}

带任务参数

import com.congge.service.QuartzJobService;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;@Component
@Slf4j
public class SampleParamJob implements Job {@Autowiredprivate QuartzJobService quartzJobService;/*** 若参数变量名修改 QuartzJobController中也需对应修改*/private String parameter;public void setParameter(String parameter) {this.parameter = parameter;}@Overridepublic void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {log.info(String.format("准备执行带参数定时任务 SampleParamJob !   时间:" + LocalDateTime.now(), this.parameter));quartzJobService.test(this.parameter);}}

5.5 效果测试

5.5.1 添加任务

使用postman调用添加任务的接口

执行成功后,从控制台可以看到,任务已经在执行了

同时数据库也增加了一条数据

5.5.2 暂停任务

浏览器调用接口:localhost:8088/job/pause?jobClassName=com.congge.job.SampleJob

调用成功后,可以从控制台看到任务已经停止执行了

其他的测试用例可以参照上面的方式继续测试即可。

5.6 补充说明

基于上面的整合和效果验证,下面留待几个问题请进一步思考和探究

问题1:

如何让任务添加成功后延迟一段时间执行?

问题2:

 如何解决任务的并发执行?

问题3:

 两次执行任务的时间间隔太短怎么办?

六、写在结尾

相对其他的分布式任务调度框架,quartz整体来说学习成本不算高,而且适用的场景也比较多,在项目中合理的引用,可以灵活的解决很多问题。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/212941.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

北邮22级信通院数电:Verilog-FPGA(12)第十二周实验(2)彩虹呼吸灯(bug已解决 更新至3.0)

北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 目录 一.代码部分 1.1一些更新和讲解 1.2改正后的…

Vue配置代理解决跨域

Network的status中报CORS error指在前端&#xff08;Vue.js&#xff09;发起跨域请求时&#xff0c;被服务器拒绝访问的错误 在本地开发环境中&#xff0c;Vue.js 将默认从 http://localhost:8080 启动服务器。如果浏览器访问服务器时使用的 URL 不是该地址&#xff0c;就可能…

selenium 解决 id定位、class定位中,属性值带空格的解决办法

一、前置说明 selenium遇到下面这种元素&#xff1a; <th id"demo id" class"value1 value2 value3 ">1、虽然id一般不会有空格&#xff0c;但是前端错误的这种写法(如下图)&#xff0c;会造成使用id定位不到元素&#xff0c;如&#xff1a; find…

Unity3D对CSV文件操作(创建、读取、写入、修改)

系列文章目录 Unity工具 文章目录 系列文章目录前言一、Csv是什么&#xff1f;二、创建csv文件2-1、构建表数据2-2、创建表方法2-3、完整的脚本&#xff08;第一种方式&#xff09;2-4、运行结果2-5、完整的脚本&#xff08;第二种方式&#xff09;2-6、运行结果2-7、想用哪种…

《Linux源码趣读》| 好书推荐

目录 一. &#x1f981; 前言二. &#x1f981; 像小说一样趣读 Linux 源码三. &#x1f981; 学习路线 一. &#x1f981; 前言 最近、道然科技给狮子送了两本书&#xff1a;一本是付东来的《labuladong的算法笔记》、一本是闪客著的《Linux源码趣读》&#xff0c;《labulado…

Python并发-线程和进程

一、线程和进程对应的问题 **1.进程&#xff1a;**CPU密集型也叫计算密集型&#xff0c;指的是系统的硬盘、内存性能相对CPU要好很多&#xff0c;此时&#xff0c;系统运作大部分的状况是CPU Loading 100%&#xff0c;CPU要读/写I/O(硬盘/内存)&#xff0c;I/O在很短的时间就可…

ZLMediakit-method ANNOUNCE failed: 401 Unauthorized(ffmpeg、obs推流rtmp到ZLM发现的问题)

错误截图 解决办法&#xff1a;能推流成功&#xff0c;但是不能写入到wvp数据库中 修改配置文件config.ini 改成0 修改之后 重启服务 systemctl restart zlm*推流成功 解决办法&#xff1a;能推流&#xff0c;能写入数据库中 替换zlm版本&#xff0c;可以用我文章中提供的编译…

【STM32入门】3.OLED屏幕

1.OLED引脚 OLED屏幕的接线按图所示&#xff0c;本例中用的是4管脚OLED屏幕 2.驱动程序 配套的驱动程序是“OLED.c"&#xff0c;主要由以下函数构成&#xff1a;1、初始化&#xff1b;2、清屏&#xff1b;3、显示字符&#xff1b;4、显示字符串&#xff1b;5、显示数字…

计算机毕业设计 基于SpringBoot的高校毕业与学位资格审核系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

LAMP安装部署网站

目录 什么是LAMP? 实验&#xff08;搭建一个论坛&#xff09; 一&#xff0c;安装apache 1.关闭防火墙&#xff0c;将安装Apache所需软件包传到/opt目录下 2.安装环境依赖包 3.配置软件模块 4.编译及安装 5.优化配置文件路径&#xff0c;并把httpd服务的可执行程序文件…

什么是网站劫持

网站劫持是一种网络安全威胁&#xff0c;它通过非法访问或篡改网站的内容来获取机密信息或者破坏计算机系统。如果您遇到了网站劫持问题&#xff0c;建议您立即联系相关的安全机构或者技术支持团队&#xff0c;以获得更专业的帮助和解决方案。

python数据分析之二、读取excel数据并绘制折线图,柱状图、饼状图

今天开始第二篇&#xff0c;也是那位可爱的同学的期末作业 题目基本描述如下&#xff1a;给一个简单的execl表格数据&#xff0c;用并列折线图&#xff0c;并列柱状图和饼图来表现数据。 现给定表格数据如下&#xff1a; 地区人口数0-14岁5-64岁65岁及以上全国14940542613671…

LeetCode124.二叉树中最大路径和

第一次只花了20分钟左右就完全靠自己把一道hard题做出来了。我这个方法还是非常简单非常容易理解的&#xff0c;虽然时间复杂度达到了O(n2)。以下是我的代码&#xff1a; class Solution {int max;public int maxPathSum(TreeNode root) {max Integer.MIN_VALUE;return dfs2(…

船舶机电设备振动数据采集监控系统解决方案

船舶运行中&#xff0c;通常需要通过振动数据采集系统对船舶的各个机电设备运行进行监控&#xff0c;有助于在设备故障时快速预警&#xff0c;进行诊断、分析和维护&#xff0c;保证船舶机电设备正常工作&#xff0c;从而确保工作人员及船舶的安全。 船舶各种机电设备会产生大…

机器连接和工业边缘计算

软件应用和IT创新是制造业投资的主要驱动力。解决方案架构应围绕特定标准进行整合&#xff0c;并采用架构蓝图和最佳实践来满足最终用户的需求。此外&#xff0c;边缘计算&#xff08;Edge Computing&#xff09;也将在制造业中加速部署。 边缘计算是制造业的下一个变革驱动力。…

PyQt基础_011_对话框类控件QMessage

基本功能 import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import *class WinForm( QWidget): def __init__(self): super(WinForm,self).__init__() self.setWindowTitle("QMessageBox") self.resize(300, 100) self.myButt…

Unity3D实现鼠标悬浮UI或物体上显示文字信息

系列文章目录 Unity工具 文章目录 系列文章目录前言最终效果一、UI事件显示文字1-1 ui事件需要引用命名空间using UnityEngine.EventSystems;1-2 IPointerEnterHandler 接口1-3 IPointerExitHandler 接口1-4 IPointerMoveHandler 接口 二、场景搭建2-1 实现如下 三、代码实现3…

C# 语法笔记

1.ref、out&#xff1a;参数传递的两种方式 ref&#xff1a;引用传递 using System; namespace CalculatorApplication {class NumberManipulator{public void swap(ref int x, ref int y){int temp;temp x; /* 保存 x 的值 */x y; /* 把 y 赋值给 x */y temp; /* 把 t…

Grad-CAM原理

这篇是我对哔哩哔哩up主 霹雳吧啦Wz 的视频的文字版学习笔记 感谢他对知识的分享 只要大家一提到深度学习 缺乏一定的解释性 比如说在我们之前讲的分类网络当中 网络它为什么要这么预测 它针对每个类别所关注的点在哪里呢 在great cam这篇论文当中呢 就完美的解决了在cam这篇论…