【微服务】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.mzph.cn/news/205110.shtml

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

相关文章

浅谈https

1.网络传输的安全性 http 协议&#xff1a;不安全&#xff0c;未加密https 协议&#xff1a;安全&#xff0c;对请求报文和响应报文做加密 2.对称加密与非对称加密 2.1 对称加密 特点&#xff1a; 加解密使用 相同 秘钥 高效&#xff0c;适用于大量数据的加密场景 算法公开&a…

C++STL的string类(一)

文章目录 前言C语言的字符串 stringstring类的常用接口string类的常见构造string (const string& str);string (const string& str, size_t pos, size_t len npos); capacitysize和lengthreserveresizeresize可以删除数据 modify尾插插入字符插入字符串 inserterasere…

7.3 Windows驱动开发:内核监视LoadImage映像回调

在笔者上一篇文章《内核注册并监控对象回调》介绍了如何运用ObRegisterCallbacks注册进程与线程回调&#xff0c;并通过该回调实现了拦截指定进行运行的效果&#xff0c;本章LyShark将带大家继续探索一个新的回调注册函数&#xff0c;PsSetLoadImageNotifyRoutine常用于注册Loa…

学习IO的第五天

作业 &#xff1a;使用两个线程完成文件的拷贝写入&#xff0c;分线程1写入前半段&#xff0c;分线程2写入后半段&#xff0c;主线程用来回收资源 #include <head.h>void *sork(void *arg); void *sork2(void *arg);int file_copy(int start,int len) //拷贝的函数 {i…

Linux_vi/vim编辑器

3.VI 与 VIM 3.1概述 vi编辑器&#xff1a;是Linux和Unix上最基本的文本编辑器&#xff0c;工作在字符模式下。由于不需要图形界面&#xff0c;vi是效率很高的文本编辑器。 vim是&#xff1a;vi的增强版&#xff0c;比vi更容易使用。vi的命令几乎全部都可以在vim上使用。 3…

Qt图形设计

#include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//窗口相关设置//设置窗口标题this->setWindowTitle("王者荣耀");//设置窗口图标this->setWindowIcon(QIcon("C:\\Users\\28033\\Pictures\\Saved Pictures\\pict…

ESP32单片机案例

工具&#xff1a;VScode PlatformIO IDE 注&#xff1a;B站视频学习笔记。 1、继电器 1&#xff09;硬件电路 2&#xff09;程序 #include <Arduino.h> #define RELAY_PIN 15//初始化定时器 hw_timer_t *timer NULL;void timer_interrupt(){ //将引脚传入的电平信号…

公众号word文档

在数字化时代&#xff0c;信息的快速获取和高效整理变得尤为重要。微信公众号作为信息传播的重要平台&#xff0c;其内容经常需要被转换成更易于编辑和存档的格式&#xff0c;如Word文档。这里&#xff0c;我们将介绍如何利用“微附件”小程序实现这一过程&#xff0c;并分享一…

第二十一章

网络通信这一章 基本分为三个部分 网络基础概念和TCP,UDP这三个部分主要如下&#xff1a; 计算机网络实现了堕胎计算机间的互联&#xff0c;使得它们彼此之间能够进行数据交流。网络应用程序就是再已连接的不同计算机上运行的程序&#xff0c;这些程序借助于网络协议&#xf…

mixamo根动画导入UE5问题:滑铲

最近想做一个跑酷游戏&#xff0c;从mixamo下载滑铲动作后&#xff0c;出了很多动画的问题。花了两周时间&#xff0c;终于是把所有的问题基本上都解决了。 常见问题&#xff1a; 1.【动画序列】人物不移动。 2.【动画序列】人物移动朝向错误。 3.【蒙太奇】人物移动后会被拉回…

谈谈 .NET8 平台中对 LiteDB 的 CRUD 操作

哪个啥&#xff01;纯 C# 编写的 LiteDB 你还不会操作&#xff1f; LiteDB 简介LiteDB 安装1、同步版 LiteDB2、异步版 LiteDB.Async LiteDB StudioLiteDB CRUD 操作举例1、.net cli 命令创建项目2、项目添加相关 nuget 包3、改造项目结构4、改造项目代码 LiteDB vs SQLite 对比…

MySQl int(1)、int(20) 的区别到底在哪里

MySQl int(1)、int(20) 的区别到底在哪里 常思一二&#xff0c;便得自然… int(1)数据类型介绍 在MySQL中&#xff0c;INT(1) 是一种定义整数类型的数据字段&#xff0c;其中的数字表示显示宽度而不是存储范围。具体说&#xff0c;INT(1) 中的数字 1 表示显示宽度&#xff0…

高级搜索——ST表,离线RMQ问题

文章目录 前言可重复贡献问题ST表的定义ST表的存储结构ST表的预处理预处理的实现 ST表的区间查询对于k的获取区间查询的实现 OJ链接 前言 对于查询区间最值的方法&#xff0c;我们常用的就是线段树&#xff0c;树状数组&#xff0c;单调队列&#xff0c;而树状数组更适合用于快…

【互斥锁不当使用导致的条件竞争】2021_DiceCTF_hashbrown

前言 这个题目还挺有意思的&#xff0c;他并不像之前做的题目直接给你一个贴脸的 UAF 等&#xff0c;而是把 UAF 放在了条件竞争的环境下&#xff0c;其实条件竞争这个漏洞在内核中经常出现。 这里题目没有去符号&#xff0c;所以逆向的难度不是很大&#xff0c;但作者似乎在…

Android蓝牙协议栈fluoride(二) - 软件框架

概述 fluoride 协议栈在整个软件框架中作为一个中间件的角色&#xff0c;向上对接APP&#xff0c;向下对接蓝牙芯片。fluoride采用C语言实现&#xff0c;与APP(Jave)通信采用JNI机制&#xff1b;与蓝牙芯片通信使用HCI硬件接口&#xff08;HCI软件协议参考蓝牙核心规范&#x…

毕设:《基于hive的音乐数据分析系统的设计与实现》

文章目录 环境启动一、爬取数据1.1、歌单信息1.2、每首歌前20条评论1.3、排行榜 二、搭建环境1.1、搭建JAVA1.2、配置hadoop1.3、配置Hadoop环境&#xff1a;YARN1.4、MYSQL1.5、HIVE(数据仓库)1.6、Sqoop&#xff08;关系数据库数据迁移&#xff09; 三、hadoop配置内存四、导…

FFA 2023 明日开幕,Flink 智能诊断、小红书流批一体实践精彩来袭

近年来&#xff0c;流批一体的技术思想势头火热&#xff0c;即“将批处理和流处理相结合&#xff0c;实现更好的数据处理能力”&#xff0c;已成功从理论层面走进现实世界。Flink 是一款高吞吐量、低延迟的流处理引擎&#xff0c;具备统一接口、高性能、低延迟、容错性和可扩展…

绑定域名简单教程

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 &#x1f324;️安装Nginx环境 &…

2024黑龙江省职业院校技能大赛信息安全管理与评估赛项规程

2024黑龙江省职业院校技能大赛暨国赛选拔赛 “GZ032信息安全管理与评估”赛项规程 极安云科专注技能竞赛&#xff0c;包含网络建设与运维和信息安全管理与评估两大赛项&#xff0c;及各大CTF&#xff0c;基于两大赛项提供全面的系统性培训&#xff0c;拥有完整的培训体系。团队…

【Qt开发流程】之元对象系统

描述 Qt的元对象系统&#xff08;Meta-Object System&#xff09;是Qt框架的核心机制之一&#xff0c;它提供了运行时类型信息&#xff08;RTTI&#xff09;和信号与槽&#xff08;Signals and Slots&#xff09;机制的支持。元对象系统在Qt中扮演了很重要的角色&#xff0c;它…