当前位置: 首页 > news >正文

Quartz的分布式功能化设计

Quartz的分布式功能化设计

文章目录

  • Quartz的分布式功能化设计
    • 主体功能
    • 实现依赖
    • API
    • 例子JOB
    • Job记录表设计
    • java具体代码
      • `DateDO`
      • `OperatorDO`
      • `SysQuartzJobDO`
      • `PageDTO`
      • `QuartzJobDTO`
      • `QuartzJobPageDTO`
      • `QuartzJobStatusEnum`
      • `QuartzJobController`
      • `IQuartzJobService`
      • `QuartzJobServiceImpl`
      • `QuartzJobMapper`
      • `QuartzJobMapper.xml`
      • `SampleJob`

quartz分布式自带的管理表位置:建表语句所在位置: quartz-x.y.z.jarorg.quartz.imp.jdbcjobstore路径下,这边只介绍Mysql的,文件名为 table_mysql_innodb.sql。在sql文件中一共有11张表。

1.qrtz_blob_triggers
2.qrtz_cron_triggers
3.qrtz_simple_triggers
4.qrtz_simprop_triggers
5.qrtz_fired_triggers
6.qrtz_triggers
7.qrtz_job_details
8.qrtz_calendars
9.qrtz_paused_trigger_grps
10.qrtz_scheduler_state
11.qrtz_locks

使用场景:所有的任务都是针对业务来的,并非公共调度平台,所以侵入式代码库是可以的。

主体功能

  1. 通过新增数据库管理表(SYS_QUARTZ_JOB),来明确任务基本信息
  2. 任务类型都是定时型;
  3. 支持新增任务,只能存在一个在用的同名任务;
  4. 支持暂停任务;
  5. 支持恢复暂停的任务;
  6. 支持更新任务,数据库管理表是更新操作,而Quartz框架是进行了先删除在重建进行更新;
  7. 支持删除任务;
  8. 支持即时执行一次任务。

实现依赖

		<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- 打war包时加入此项, 告诉spring-boot tomcat相关jar包用外部的,不要打进去 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><scope>provided</scope><!-- 校验帮助包 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><!-- 任务调度quartz--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency><!-- 数据源相关 分页插件page helper --><dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>${pagehelper-spring-boot-starter.version}</version></dependency><dependency><groupId>tk.mybatis</groupId><artifactId>mapper-spring-boot-starter</artifactId><version>${mapper-spring-boot-starter.version}</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>${mybatis-spring-boot-starter.verson}</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql-connector-java.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>${druid-spring-boot-starter.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>${fastjson2.version}</version></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>${gooogle-guava.version}</version></dependency>

springboot工程的集成quartz配置
application.properties

# quartz配置
spring.quartz.job-store-type=jdbc
spring.quartz.jdbc.schema=never
spring.quartz.properties.org.quartz.scheduler.instanceName=quartzScheduler
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO
spring.quartz.properties.org.quartz.jobStore.dataSource=mysql
spring.quartz.properties.org.quartz.jobStore.class=org.springframework.scheduling.quartz.LocalDataSourceJobStore
spring.quartz.properties.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
spring.quartz.properties.org.quartz.jobStore.tablePrefix=QRTZ_
spring.quartz.properties.org.quartz.jobStore.isClustered=true
spring.quartz.properties.org.quartz.jobStore.misfireThreshold=12000
spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval=15000
spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
spring.quartz.properties.org.quartz.threadPool.threadCount=1
spring.quartz.properties.org.quartz.threadPool.threadPriority=5
spring.quartz.properties.org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true

API

  1. 新建Quartz任务:addJob(@Validated @RequestBody QuartzJobDTO quartzJobDTO,HttpServletRequest req)
  2. 立即执行任务:runJobNow(@NotNull(message = "jobId不能为空") Long jobId, HttpServletRequest req)
  3. 删除Quartz任务:deleteJob(@NotNull(message = "jobId不能为空") Long jobId, HttpServletRequest req)
  4. 恢复Quartz任务(针对暂停的任务):resumeJob(@NotBlank(message = "任务完整类名不能为空") @Size(max = 250, message = "最大长度250") String jobClassName, HttpServletRequest req)
  5. 暂停Quartz任务:pauseJob(@NotBlank(message = "任务完整类名不能为空") @Size(max = 250, message = "最大长度250") String jobClassName, HttpServletRequest req)
  6. 查询未删除的Quartz任务 分页式:quartzJoblist(@Validated @RequestBody QuartzJobPageDTO quartzJobPageDto)
  7. 更新Quartz任务(采用先删除后增加的方式处理):updateJob(@Validated @RequestBody UpdateQuartzJobDTO quartzJob, HttpServletRequest req)

例子JOB

SampleJob

新增JOB_API请求体

{"jobClassName": "com.donny.web.quartz.jobs.SampleJob","cronExpression": "0/20 * * * * ? ","description":"测试简单Quartz任务,20s执行一次","status": 1
}

Job记录表设计

CREATE TABLE t_sys_quartz_job
(`id`              BIGINT unsigned PRIMARY KEY AUTO_INCREMENT COMMENT '主键',`create_time`     DATETIME         NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time`     DATETIME         NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',`create_user`     VARCHAR(12)      NOT NULL COMMENT '创建人',`update_user`     VARCHAR(12)      NOT NULL COMMENT '修改人',`job_class_name`  VARCHAR(250)     NOT NULL COMMENT '任务的类名',`cron_expression` VARCHAR(250)     NULL COMMENT '任务的cron表达式',`description`     VARCHAR(250)     NULL COMMENT '任务的简要描述',`status`          tinyint unsigned NOT NULL DEFAULT '0' COMMENT '任务的状态,0:正常,1:停止',`is_deleted`      tinyint unsigned NOT NULL DEFAULT '0' COMMENT '任务是否已删除,0:否,1:是'
) ENGINE = InnoDB COMMENT ='quartz任务记录表';

java具体代码

DateDO

package com.donny.web.model.entity;import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;import javax.persistence.Column;
import java.io.Serializable;
import java.util.Date;/*** 数据库基础日期字段类,主要针对数据库记录的两个日期字段** @author donny* @version 1.0* @since 2024年01月17日 14:37*/
@Data
public class DateDO implements Serializable {/*** 创建时间*/@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")@Column(name = "create_time")private Date createTime;/*** 更新时间*/@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")@Column(name = "update_time")private Date updateTime;
}

OperatorDO

package com.donny.web.model.entity;import lombok.Data;
import lombok.EqualsAndHashCode;import javax.persistence.Column;
import java.io.Serializable;/*** 数据库基础字段操作者类,主要针对更新记录操作人4个字段** @author donny* @version 1.0* @since 2024年01月17日 14:38*/
@Data
@EqualsAndHashCode(callSuper = true)
public class OperatorDO extends DateDO implements Serializable {/*** 创建者工号*/@Column(name = "create_user")private String createUser;/*** 更新者工号*/@Column(name = "update_user")private String updateUser;
}

SysQuartzJobDO

package com.donny.web.model.entity;import lombok.Data;
import lombok.EqualsAndHashCode;import javax.persistence.*;
import java.io.Serializable;/*** sys_quartz_job的表映射Entity** @author donny* @version 1.0* @since 2023/12/27*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table(name = "t_sys_quartz_job")
public class SysQuartzJobDO extends OperatorDO implements Serializable {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "id")private Long id;/*** 任务类名*/@Column(name = "job_class_name")private String jobClassName;/*** cron表达式*/@Column(name = "cron_expression")private String cronExpression;/*** 描述*/@Column(name = "description")private String description;/*** 状态 0正常 1停止*/@Column(name = "status")private Integer status;/*** 逻辑删除标记*/@Column(name = "is_deleted")private Integer isDeleted;}

PageDTO

package com.donny.web.model.dto;import lombok.Getter;
import lombok.Setter;import javax.validation.constraints.Max;
import javax.validation.constraints.NotNull;/*** 分页查询的关于“页”的条件,供具体分页查询的业务DTO继承** @author Donny* @version 1.0* @since 2023年12月27日 16:19*/
@Setter
@Getter
public class PageDTO {/*** 当前页码*/@NotNull(message = "currentPage,当前页码不能为空")private Integer currentPage;/*** 每页记录数*/@NotNull(message = "pageSize,每页记录数不能为空")@Max(value = 50, message = "最大长度为50")private Integer pageSize;@Overridepublic String toString() {return "PageDTO{" +"currentPage=" + currentPage +", pageSize=" + pageSize +'}';}
}

QuartzJobDTO

package com.donny.web.model.dto;import lombok.Getter;
import lombok.Setter;import javax.validation.constraints.*;/*** 供api使用* SysQuartzJob对象可供api设置的字段集合对象** @author Donny* @version 1.0* @since 2023/12/27*/
@Setter
@Getter
public class QuartzJobDTO {/*** 创建者工号*/private String createUser;/*** 更新者工号*/private String updateUser;/*** 任务完整类名*/@NotBlank(message = "jobClassName,不能为空")@Size(max = 250, message = "jobClassName最大长度250")private String jobClassName;/*** 调度周期 cron表达式*/@NotBlank(message = "调度周期cron表达式不能为空")@Size(max = 250, message = "cronExpression最大长度250")private String cronExpression;/*** 任务备注信息*/@Size(max = 250, message = "jobClassName最大长度250")private String description;/*** 状态 0正常 1停止*/@NotNull(message = "status,状态不能为空")@Max(value = 1, message = "status不超过1")@Min(value = 0, message = "status不低于0")private Integer status;/*** 逻辑删除标记*/private Integer isDeleted;@Overridepublic String toString() {return "QuartzJobDTO{" +", createUser='" + createUser + '\'' +", updateUser='" + updateUser + '\'' +", jobClassName='" + jobClassName + '\'' +", cronExpression='" + cronExpression + '\'' +", description='" + description + '\'' +", status=" + status +", isDeleted=" + isDeleted +'}';}
}

QuartzJobPageDTO

package com.donny.web.model.dto;import com.fasterxml.jackson.annotation.JsonFormat;import java.util.Date;/*** 供api使用* QuartzJob列表查询条件对象** @author Donny* @version 1.0* @since 2023年12月28日 10:24*/
public class QuartzJobPageDTO extends PageDTO {/*** 任务类名*/private String jobClassName;/*** 状态 0正常 1停止*/private Integer status;/*** 创建时间*/@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private Date startTime;/*** 修改时间*/@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private Date endTime;public String getJobClassName() {return jobClassName;}public void setJobClassName(String jobClassName) {this.jobClassName = jobClassName;}public Integer getStatus() {return status;}public void setStatus(Integer status) {this.status = status;}public Date getStartTime() {return null == startTime ? null : (Date) startTime.clone();}public void setStartTime(Date startTime) {this.startTime = null == startTime ? null : (Date) startTime.clone();}public Date getEndTime() {return null == endTime ? null : (Date) endTime.clone();}public void setEndTime(Date endTime) {this.endTime = null == endTime ? null : (Date) endTime.clone();}@Overridepublic String toString() {return "QuartzJobPageDTO{" +"jobClassName='" + jobClassName + '\'' +", status=" + status +", startTime=" + startTime +", endTime=" + endTime +", currentPage=" + super.getCurrentPage() +", pageSize=" + super.getPageSize() +"}";}
}

QuartzJobStatusEnum

package com.donny.web.quartz;/*** QuartzJob的Status 枚举值** @author donny* @version 1.0* @since 2023年12月27日 16:30*/
public enum QuartzJobStatusEnum {NORMAL(0),STOPPED(1);private int status;QuartzJobStatusEnum(int status) {this.status = status;}public int getValue() {return status;}public static String getDescription(QuartzJobStatusEnum status) {String description = "待设状态";switch (status) {case NORMAL:description = "正常";break;case STOPPED:description = "已停止";break;default:break;}return description;}
}

QuartzJobController

package com.donny.web.controller.manager;import com.donny.web.model.dto.QuartzJobPageDTO;
import com.donny.web.model.dto.UpdateQuartzJobDTO;
import com.donny.web.platform.pagehelper.PageBean;
import com.donny.web.model.dto.QuartzJobDTO;
import com.donny.web.model.entity.SysQuartzJobDO;
import com.donny.web.platform.ResponseBuilder;
import com.donny.web.platform.ReturnCode;
import com.donny.web.platform.WebReturnCode;
import com.donny.web.quartz.IQuartzJobService;
import com.donny.web.utils.PortalSessionUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;/*** quartz的管理入口* 主体功能:<p>* 通过新增数据库管理表(SYS_QUARTZ_JOB),来明确任务基本信息<p>* 0.任务类型都是定时型;<p>* 1.支持新增任务,只能存在一个在用的同名任务;<p>* 2.支持暂停任务;<p>* 3.支持恢复暂停的任务;<p>* 4.支持更新任务,数据库管理表是更新操作,而Quartz框架是进行了先删除在重建进行更新;<p>* 5.支持删除任务;<p>* 6.支持即时执行一次任务。** @author Donny* @version 1.0* @since 2023/12/26*/
@RestController
@Validated
@RequestMapping(value = "/api/v2/manager/quartz")
@Slf4j
public class QuartzJobController {@ResourceIQuartzJobService quartzJobService;/*** 新建Quartz任务** @param quartzJobDTO {@link QuartzJobDTO}*/@PostMapping(value = "/addJob")public ResponseEntity<ReturnCode> addJob(@Validated @RequestBody QuartzJobDTO quartzJobDTO,HttpServletRequest req) {String user = PortalSessionUtils.getLoginUserId(req);quartzJobDTO.setCreateUser(user);quartzJobDTO.setUpdateUser(user);if (quartzJobService.addJob(quartzJobDTO)) {log.info("QuartzJob:[{}]已新增。", quartzJobDTO.getJobClassName());return ResponseBuilder.build(WebReturnCode.SUCCEED, "新增成功", null);} else {return ResponseBuilder.build(WebReturnCode.FAILED, "新增失败", null);}}/*** 更新Quartz任务*/@PostMapping(value = "/updateJob")public ResponseEntity<ReturnCode> updateJob(@Validated @RequestBody UpdateQuartzJobDTO quartzJob,HttpServletRequest req) {quartzJob.setUpdateUser(PortalSessionUtils.getLoginUserId(req));if (quartzJobService.updateQuartzJob(quartzJob)) {log.info("QuartzJob:[{}]已更新。", quartzJob.getJobClassName());return ResponseBuilder.build(WebReturnCode.SUCCEED, "更新成功", null);} else {return ResponseBuilder.build(WebReturnCode.FAILED, "更新失败", null);}}/*** 查询未删除的Quartz任务 分页式*/@PostMapping(value = "/joblist")public ResponseEntity<ReturnCode> quartzJoblist(@Validated @RequestBody QuartzJobPageDTO quartzJobPageDto) {PageBean pageBean = new PageBean(quartzJobPageDto.getCurrentPage(), quartzJobPageDto.getPageSize());return ResponseBuilder.build(WebReturnCode.SUCCEED, quartzJobService.listAll(quartzJobPageDto, pageBean));}/*** 暂停Quartz任务** @param jobClassName job的类名(完成路径)*/@GetMapping(value = "/pauseJob")public ResponseEntity<ReturnCode> pauseJob(@NotBlank(message = "任务完整类名不能为空")@Size(max = 250, message = "最大长度250")String jobClassName,HttpServletRequest req) {SysQuartzJobDO job = quartzJobService.findByJobClassName(jobClassName);if (null == job) {return ResponseBuilder.build(WebReturnCode.FAILED, "暂停失败,不存在该任务", null);}job.setUpdateUser(PortalSessionUtils.getLoginUserId(req));if (quartzJobService.pauseJob(job)) {log.info("QuartzJob:[{}]已暂停。", jobClassName);return ResponseBuilder.build(WebReturnCode.SUCCEED, "暂停成功", null);} else {return ResponseBuilder.build(WebReturnCode.FAILED, "暂停失败", null);}}/*** 恢复Quartz任务** @param jobClassName job的类名(完整路径)*/@GetMapping(value = "/resumeJob")public ResponseEntity<ReturnCode> resumeJob(@NotBlank(message = "任务完整类名不能为空")@Size(max = 250, message = "最大长度250")String jobClassName,HttpServletRequest req) {SysQuartzJobDO job = quartzJobService.findByJobClassName(jobClassName);if (null == job) {return ResponseBuilder.build(WebReturnCode.FAILED, "恢复失败,不存在该任务", null);}job.setUpdateUser(PortalSessionUtils.getLoginUserId(req));if (quartzJobService.resumeJob(job)) {log.info("QuartzJob:[{}]已恢复。", jobClassName);return ResponseBuilder.build(WebReturnCode.SUCCEED, "恢复成功", null);} else {return ResponseBuilder.build(WebReturnCode.FAILED, "恢复失败", null);}}/*** 逻辑删除Quartz任务** @param jobId Quartz任务的主键*/@GetMapping(value = "/deleteJob")public ResponseEntity<ReturnCode> deleteJob(@NotNull(message = "jobId不能为空")Long jobId,HttpServletRequest req) {SysQuartzJobDO quartzJob = quartzJobService.getJobById(jobId);if (quartzJob == null) {return ResponseBuilder.build(WebReturnCode.SUCCEED, "不存在该任务", null);}quartzJob.setUpdateUser(PortalSessionUtils.getLoginUserId(req));if (quartzJobService.logicDeleteAndStopJob(quartzJob)) {log.info("QuartzJob:[{}]已删除。", quartzJob.getJobClassName());return ResponseBuilder.build(WebReturnCode.SUCCEED, "删除成功", null);} else {return ResponseBuilder.build(WebReturnCode.FAILED, "删除失败", null);}}/*** 立即尝试执行一个任务** @param jobId Quartz任务的主键*/@GetMapping(value = "/runJobNow")public ResponseEntity<ReturnCode> runJobNow(@NotNull(message = "jobId不能为空")Long jobId,HttpServletRequest req) {SysQuartzJobDO quartzJob = quartzJobService.getJobById(jobId);if (quartzJob == null) {return ResponseBuilder.build(WebReturnCode.FAILED, "不存在该任务", null);}if (quartzJobService.runJobNow(quartzJob)) {log.info("QuartzJob:[{}]已执行。操作人[{}]。", quartzJob.getJobClassName(), PortalSessionUtils.getLoginUserId(req));return ResponseBuilder.build(WebReturnCode.SUCCEED, "执行成功", null);} else {return ResponseBuilder.build(WebReturnCode.FAILED, "执行失败", null);}}/*** 根据主键查询任务信息** @param id {@link UpdateQuartzJobDTO#getId()}*/@GetMapping(value = "/get")public ResponseEntity<ReturnCode> getQuartzJob(@NotNull(message = "id不能为空")Long id) {try {return ResponseBuilder.build(WebReturnCode.SUCCEED, this.quartzJobService.getJobById(id));} catch (Exception e) {log.error("查询失败:" + e.getMessage(), e);return ResponseBuilder.build(WebReturnCode.FAILED, "查询失败:" + e.getMessage(), null);}}
}

IQuartzJobService

package com.donny.web.quartz;import com.donny.web.model.dto.QuartzJobDTO;
import com.donny.web.model.dto.QuartzJobPageDTO;
import com.donny.web.model.entity.SysQuartzJobDO;
import com.donny.web.platform.pagehelper.PageBean;
import com.donny.web.platform.pagehelper.PageResult;/*** QuartzJob 服务层接口** @author donny* @version 1.0* @since 2023/12/26*/
public interface IQuartzJobService {/*** 通过任务类名查询任务** @param jobClassName 任务的*/SysQuartzJobDO findByJobClassName(String jobClassName);/*** 通过任务id查询任务** @param id 任务的数据库主键*/SysQuartzJobDO getJobById(Long id);/*** 查询未删除的Quartz任务 分页式*/PageResult<SysQuartzJobDO> listAll(QuartzJobPageDTO quartzJobPageDto, PageBean pageBean);/*** 新增任务** @param quartzJob {@link QuartzJobDTO }*/boolean addJob(QuartzJobDTO quartzJob);/*** 更新任务** @param quartzJob {@link QuartzJobDTO }*/boolean updateQuartzJob(QuartzJobDTO quartzJob);/*** 逻辑删除任务** @param quartzJob {@link SysQuartzJobDO }*/boolean logicDeleteAndStopJob(SysQuartzJobDO quartzJob);/*** 暂停任务** @param quartzJob {@link SysQuartzJobDO }*/boolean pauseJob(SysQuartzJobDO quartzJob);/*** 恢复任务** @param quartzJob {@link SysQuartzJobDO }*/boolean resumeJob(SysQuartzJobDO quartzJob);/*** 立即执行任务** @param quartzJob {@link SysQuartzJobDO }*/boolean runJobNow(SysQuartzJobDO quartzJob);
}

QuartzJobServiceImpl

package com.donny.web.quartz;import com.github.pagehelper.Page;
import com.github.pagehelper.page.PageMethod;
import com.donny.web.dao.mysql.QuartzJobMapper;
import com.donny.web.model.dto.QuartzJobDTO;
import com.donny.web.model.dto.QuartzJobPageDTO;
import com.donny.web.model.dto.UpdateQuartzJobDTO;
import com.donny.web.model.entity.SysQuartzJobDO;
import com.donny.web.platform.exception.BusinessException;
import com.donny.web.platform.pagehelper.PageBean;
import com.donny.web.platform.pagehelper.PageResult;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import javax.annotation.Resource;/*** QuartzJob 服务层实现** @author donny* @version 1.0* @since 2023/12/26*/
@Service
public class QuartzJobServiceImpl implements IQuartzJobService {private static final Logger LOGGER = LoggerFactory.getLogger(QuartzJobServiceImpl.class);@Resourceprivate Scheduler scheduler;private final QuartzJobMapper quartzJobMapper;@Autowiredpublic QuartzJobServiceImpl(QuartzJobMapper quartzJobMapper) {this.quartzJobMapper = quartzJobMapper;}@Overridepublic SysQuartzJobDO findByJobClassName(String jobClassName) {return this.quartzJobMapper.findByJobClassName(jobClassName);}@Overridepublic SysQuartzJobDO getJobById(Long id) {return this.quartzJobMapper.getJobById(id);}@Overridepublic PageResult<SysQuartzJobDO> listAll(QuartzJobPageDTO quartzJobPageDto, PageBean pageBean) {try (Page<SysQuartzJobDO> page = PageMethod.startPage(pageBean.getCurrentPage(), pageBean.getPageSize())) {this.quartzJobMapper.listAll(quartzJobPageDto);return new PageResult<>(page.getResult(), page.getPageNum(), page.getPageSize(), page.getTotal());}}@Overridepublic boolean addJob(QuartzJobDTO quartzJob) {SysQuartzJobDO result = this.quartzJobMapper.findByJobClassName(quartzJob.getJobClassName().trim());if (null != result) {return false;}schedulerAdd(quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim());int count = this.quartzJobMapper.insert(quartzJob);return count == 1;}/*** [Quartz框架] 添加定时任务*/private void schedulerAdd(String jobClassName, String cronExpression) {try {// 启动调度器this.scheduler.start();// 构建job信息JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(jobClassName).build();// 表达式调度构建器(即任务执行的时间)CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);// 按新的cronExpression表达式构建一个新的triggerCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobClassName).withSchedule(scheduleBuilder).build();this.scheduler.scheduleJob(jobDetail, trigger);} catch (SchedulerException e) {LOGGER.warn("[Quartz Scheduler] 创建定时任务失败" + e.getMessage(), e);throw new BusinessException("[Quartz Scheduler] 创建定时任务失败", e);}}private static Job getClass(String classname) {try {Class<?> class1 = Class.forName(classname);return (Job) class1.newInstance();} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {LOGGER.warn("[Quartz Scheduler] getClass获取对应类实例失败" + e.getMessage(), e);throw new BusinessException("[Quartz Scheduler] getClass获取对应类实例失败");}}/*** [Quartz框架]删除定时任务*/private void schedulerDelete(String jobClassName) {try {/*使用给定的键暂停Trigger 。*/this.scheduler.pauseTrigger(TriggerKey.triggerKey(jobClassName));/*从调度程序中删除指示的Trigger */this.scheduler.unscheduleJob(TriggerKey.triggerKey(jobClassName));/*从 Scheduler 中删除已识别的Job - 以及任何关联的Trigger */this.scheduler.deleteJob(JobKey.jobKey(jobClassName));} catch (SchedulerException e) {LOGGER.warn("[Quartz Scheduler] 删除定时任务失败" + e.getMessage(), e);throw new BusinessException("[Quartz Scheduler] 删除定时任务失败");}}@Overridepublic boolean updateQuartzJob(QuartzJobDTO quartzJob) throws BusinessException {try {schedulerDelete(quartzJob.getJobClassName().trim());schedulerAdd(quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim());if (QuartzJobStatusEnum.NORMAL.getValue() != quartzJob.getStatus()) {this.scheduler.pauseJob(JobKey.jobKey(quartzJob.getJobClassName().trim()));}} catch (SchedulerException e) {LOGGER.warn("[Quartz Scheduler] 更新定时任务失败" + e.getMessage(), e);throw new BusinessException("[Quartz Scheduler] 更新定时任务失败");}int count = this.quartzJobMapper.update(quartzJob);return count == 1;}@Overridepublic boolean logicDeleteAndStopJob(SysQuartzJobDO quartzJob) {schedulerDelete(quartzJob.getJobClassName().trim());int count = this.quartzJobMapper.logicDelete(quartzJob.getId(), quartzJob.getUpdateUser());return count == 1;}@Overridepublic boolean pauseJob(SysQuartzJobDO quartzJob) throws BusinessException {try {this.scheduler.pauseJob(JobKey.jobKey(quartzJob.getJobClassName().trim()));} catch (SchedulerException e) {LOGGER.warn("[Quartz Scheduler] 暂停定时任务失败" + e.getMessage(), e);throw new BusinessException("[Quartz Scheduler] 暂停定时任务失败");}UpdateQuartzJobDTO quartzJobDto = new UpdateQuartzJobDTO();quartzJobDto.setId(quartzJob.getId());quartzJobDto.setStatus(QuartzJobStatusEnum.STOPPED.getValue());int count = this.quartzJobMapper.update(quartzJobDto);return count == 1;}@Overridepublic boolean resumeJob(SysQuartzJobDO quartzJob) throws BusinessException {try {this.scheduler.resumeJob(JobKey.jobKey(quartzJob.getJobClassName().trim()));} catch (SchedulerException e) {LOGGER.warn("[Quartz Scheduler] 恢复定时任务失败" + e.getMessage(), e);throw new BusinessException("[Quartz Scheduler] 恢复定时任务失败");}UpdateQuartzJobDTO quartzJobDto = new UpdateQuartzJobDTO();quartzJobDto.setId(quartzJob.getId());quartzJobDto.setStatus(QuartzJobStatusEnum.NORMAL.getValue());quartzJobDto.setUpdateUser(quartzJob.getUpdateUser());int count = this.quartzJobMapper.update(quartzJobDto);return count == 1;}@Overridepublic boolean runJobNow(SysQuartzJobDO quartzJob) {boolean flag = true;try {JobKey jobKey = JobKey.jobKey(quartzJob.getJobClassName().trim());this.scheduler.triggerJob(jobKey);} catch (SchedulerException e) {flag = false;LOGGER.warn("[Quartz Scheduler] 尝试执行任务失败" + e.getMessage(), e);}return flag;}
}

QuartzJobMapper

package com.donny.web.dao.mysql;import com.donny.web.model.dto.QuartzJobDTO;
import com.donny.web.model.dto.QuartzJobPageDTO;
import com.donny.web.model.entity.SysQuartzJobDO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;import java.util.List;/*** @author donny* @version 1.0* @since 2023/12/26*/
@Repository
public interface QuartzJobMapper {int insert(@Param("quartzJob") QuartzJobDTO quartzJob);int update(@Param("quartzJob") QuartzJobDTO quartzJob);int logicDelete(@Param("id") Long id, @Param("updateUser") String updateUser);int physicalDelete(@Param("id") Long id);/*** 根据job的ID主键查询未删除的任务** @param id job的ID主键*/SysQuartzJobDO getJobById(@Param("id") Long id);/*** 根据jobClassName查询存在的正常的未删除的job** @param jobClassName job的类名(完成路径)*/SysQuartzJobDO findByJobClassName(@Param("jobClassName") String jobClassName);/*** 查询未删除的** @param quartzJobPageDto 查询条件对象*/List<SysQuartzJobDO> listAll(@Param("quartzJobPageDto") QuartzJobPageDTO quartzJobPageDto);
}

QuartzJobMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.donny.web.dao.mysql.QuartzJobMapper"><!--定义查询数据库时,表字段与java对象成员变量名的对应关系--><resultMap type="com.donny.web.model.entity.SysQuartzJobDO" id="quartzJobMap"><id column="id" property="id"/><result column="job_class_name" property="jobClassName"/><result column="cron_expression" property="cronExpression"/><result column="description" property="description"/><result column="status" property="status"/><result column="is_deleted" property="isDeleted"/><result column="create_time" property="createTime"/><result column="update_time" property="updateTime"/><result column="create_user" property="createUser"/><result column="update_user" property="updateUser"/></resultMap><insert id="insert" parameterType="com.donny.web.model.dto.QuartzJobDTO">insert into t_sys_quartz_job(create_user, update_user, job_class_name, cron_expression, `description`)values (#{quartzJob.createUser},#{quartzJob.updateUser},#{quartzJob.jobClassName},#{quartzJob.cronExpression},#{quartzJob.description})</insert><update id="update" parameterType="com.donny.web.model.dto.QuartzJobDTO">update t_sys_quartz_job sqj<set><if test="quartzJob.cronExpression != null and quartzJob.cronExpression != ''">sqj.cron_expression = #{quartzJob.cronExpression},</if><if test="quartzJob.description != null and quartzJob.description != ''">sqj.`description` = #{quartzJob.description},</if><if test="quartzJob.isDeleted != null and quartzJob.isDeleted != ''">sqj.is_deleted = #{quartzJob.isDeleted},</if><if test="quartzJob.status != null">sqj.`status` = #{quartzJob.status},</if><if test="quartzJob.updateUser != null and quartzJob.updateUser != ''">sqj.update_user = #{quartzJob.updateUser},</if>sqj.update_time = NOW()</set>where sqj.id = #{quartzJob.id}</update><update id="logicDelete">update t_sys_quartz_job sqj<set>sqj.update_time = NOW(),sqj.update_user = #{updateUser},sqj.is_deleted = 1</set>where sqj.id = #{id}</update><delete id="physicalDelete">deletefrom t_sys_quartz_jobwhere id = #{id}</delete><select id="findByJobClassName" resultMap="quartzJobMap">select sqj.id,create_time, update_time, create_user, update_user,job_class_name, cron_expression, `description`, `status`, is_deletedfrom t_sys_quartz_job sqj<where>sqj.is_deleted = 0<if test="jobClassName != null and jobClassName != ''">and sqj.job_class_name=#{jobClassName}</if></where></select><select id="listAll" parameterType="com.donny.web.model.dto.QuartzJobPageDTO" resultMap="quartzJobMap">select sqj.id,create_time, update_time, create_user, update_user,job_class_name, cron_expression, `description`, `status`, is_deletedfrom t_sys_quartz_job sqj<where>sqj.is_deleted=0<if test="quartzJobPageDto.jobClassName != null and quartzJobPageDto.jobClassName != ''">and sqj.job_class_name LIKE CONCAT(#{quartzJobPageDto.jobClassName}, '%')</if><if test="quartzJobPageDto.status != null">and sqj.`status` = #{quartzJobPageDto.status}</if><if test="quartzJobPageDto.startTime != null"><![CDATA[ and sqj.create_time >= #{quartzJobPageDto.startTime} ]]></if><if test="quartzJobPageDto.endTime != null"><![CDATA[ and sqj.create_time <= #{quartzJobPageDto.endTime} ]]></if></where>order by sqj.id desc</select><select id="getJobById" resultMap="quartzJobMap">select sqj.id,create_time,update_time,create_user,update_user,job_class_name,cron_expression,`description`,`status`,is_deletedfrom t_sys_quartz_job sqjwhere sqj.id = #{id}and sqj.is_deleted = 0</select></mapper>

SampleJob

package com.donny.web.quartz.jobs;import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;/*** @author donny* @version 1.0* @since 2023年12月27日 15:58*/
@Slf4j
public class SampleJob implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {log.info("SampleJob is execute");}
}

相关文章:

Quartz的分布式功能化设计

Quartz的分布式功能化设计 文章目录 Quartz的分布式功能化设计主体功能实现依赖API例子JOBJob记录表设计java具体代码DateDOOperatorDOSysQuartzJobDOPageDTOQuartzJobDTOQuartzJobPageDTOQuartzJobStatusEnumQuartzJobControllerIQuartzJobServiceQuartzJobServiceImplQuartzJ…...

Caffeine缓存

本地缓存基于本地环境的内存&#xff0c;访问速度非常快&#xff0c;对于一些变更频率低、实时性要求低的数据&#xff0c;可以放在本地缓存中&#xff0c;提升访问速度 使用本地缓存能够减少和Redis类的远程缓存间的数据交互&#xff0c;减少网络 I/O 开销&#xff0c;降低这…...

AI辅助研发正在成为造福人类的新生科技力量

目录 1.AI用于药物研发 &#xff08;1&#xff09;药物靶点预测&#xff1a; &#xff08;2&#xff09;药物分子设计&#xff1a; &#xff08;3&#xff09;药物筛选&#xff1a; &#xff08;4&#xff09;药效和安全性预测&#xff1a; &#xff08;5&#xff09…...

程序分享--排序算法--归并排序

关注我&#xff0c;持续分享逻辑思维&管理思维&#xff1b; 可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导&#xff1b; 有意找工作的同学&#xff0c;请参考博主的原创&#xff1a;《面试官心得--面试前应该如何准备》&#xff0c;《面试官心得--面试时如何进行自…...

pg数据库和mysql区别

区别一 PostgreSQL (通常称为 PG) 和 MySQL 都是广泛使用的关系型数据库管理系统 (RDBMS)。虽然它们都是用于存储和管理数据的关系数据库&#xff0c;但它们在一些方面有很大的区别&#xff0c;如下所述&#xff1a; 数据类型&#xff1a;PostgreSQL 支持更多的数据类型&#…...

Jetpack Compose 动画正式开始学习

1. 简单值动画 //将一个Color简单值 从一个值 变化到另一个 另一个简单值 就用 animateColorAsStateval backgroundColor by animateColorAsState(if (tabPage TabPage.Home) Purple100 else Green300) 动画其实就是 一个状态不停发生改变导致 组件不断重组产生的过程 2.…...

iOS 17.4报错: libopencore-amrnb.a[arm64]

iOS 17.4报错&#xff1a; libopencore-amrnb.a[arm64] iOS 17.4 模拟器运行报错解决方案 iOS 17.4 模拟器运行报错 Building for ‘iOS-simulator’, but linking in object file (/XXX/lib/libopencore-amrnb.a[arm64]2) built for ‘iOS’ 解决方案 在Podfile里添加如下设…...

鼓楼夜市管理wpf+sqlserver

鼓楼夜市管理系统wpfsqlserver 下载地址:鼓楼夜市管理系统wpfsqlserver 说明文档 运行前附加数据库.mdf&#xff08;或sql生成数据库&#xff09; 主要技术&#xff1a; 基于C#wpf架构和sql server数据库 功能模块&#xff1a; 登录注册 鼓楼夜市管理系统主界面所有店铺信…...

【五、接口自动化测试】5分钟掌握python + requests接口测试

你好啊&#xff01;我是山茶&#xff0c;一个持续探索AI 测试的程序员&#xff01; 在做接口测试时&#xff0c;在python中内置了HTTP库 urllib&#xff0c;可以用于发送http请求。基于urllib二次封装的三方库Requests&#xff0c;相较于urllib更佳简介易用。所以&#xff0c;…...

双边市场的基本理论

双边市场由两个不同类型的用户组成&#xff0c;通过一个中介机构或平台进行交易&#xff0c;其中一边用户的决策会影响另一边用户的结果。这种影响被称为间接网络外部性&#xff0c;它导致了平台在吸引和平衡两边用户时面临的挑战。 平台定价在双边市场中成为核心问题&#xf…...

R统计学2 - 数据分析入门问题21-40

往期R统计学文章&#xff1a; R统计学1 - 基础操作入门问题1-20 21. 如何对矩阵按行 (列) 作计算&#xff1f; 使用函数 apply() vec 1:20 # 转换为矩阵 mat matrix (vec , ncol4) # [,1] [,2] [,3] [,4] # [1,] 1 6 11 16 # [2,] 2 7 12 17 # [3,] …...

蓝桥杯2023年-买瓜(dfs,类型转换同样耗时)

题目描述 小蓝正在一个瓜摊上买瓜。瓜摊上共有 n 个瓜&#xff0c;每个瓜的重量为 Ai 。 小蓝刀功了得&#xff0c;他可以把任何瓜劈成完全等重的两份&#xff0c;不过每个瓜只能劈一刀。 小蓝希望买到的瓜的重量的和恰好为 m 。 请问小蓝至少要劈多少个瓜才能买到重量恰好…...

生成式人工智能服务安全基本要求实务解析

本文尝试明晰《基本要求》的出台背景与实践定位&#xff0c;梳理《基本要求》所涉的各类安全要求&#xff0c;以便为相关企业遵循执行《基本要求》提供抓手。 引言 自2022年初以来&#xff0c;我国陆续发布算法推荐、深度合成与生成式人工智能服务相关的规范文件&#xff0c;…...

nginx详解,配置http,https,负载均衡,反向代理,SMTP 代理步骤说明

Nginx 是一款高性能的开源 Web 服务器,同时也可以用作反向代理服务器、负载均衡器、HTTP 缓存、HTTPS 中继、以及作为邮件代理服务器等。以下是 Nginx 可以实现的一些常见用途: 静态内容服务: Nginx 可以用来提供静态内容,比如 HTML、CSS、JavaScript 文件等。 动态内容服务…...

ARTS Week 20

Algorithm 本周的算法题为 1222. 可以攻击国王的皇后 在一个 下标从 0 开始 的 8 x 8 棋盘上&#xff0c;可能有多个黑皇后和一个白国王。 给你一个二维整数数组 queens&#xff0c;其中 queens[i] [xQueeni, yQueeni] 表示第 i 个黑皇后在棋盘上的位置。还给你一个长度为 2 的…...

python如何读取文件

这里的文件是txt文件&#xff0c;office文件不支持。 假如有一个pi_digits的txt文件&#xff0c;里面的内容是“3.1415926” 如果要读取这个文件的内容&#xff0c;需要调取pathlib模块&#xff0c;并把路径告知python。同时python文件必须要和目标读取文件在一个文件夹里。 …...

InnoDB和MyISAM存储引擎

InnoDB mysql默认存储引擎 支持事务&#xff0c;行级锁&#xff08;并发量大&#xff09;&#xff0c;外键约束&#xff0c;容量大&#xff0c;支持缓存&#xff0c;支撑主键自增&#xff0c; 全文检索&#xff0c;不存储表的总行数&#xff0c;需要sql逐行统计 MyISAM 不…...

DataGrip 2023:让数据库开发变得更简单、更高效 mac/win

JetBrains DataGrip 2023是一款功能强大的数据库IDE&#xff0c;专为数据库开发和管理而设计。通过DataGrip&#xff0c;您可以连接到各种关系型数据库管理系统(RDBMS)&#xff0c;并使用其提供的一组工具来查询、管理、编辑和开发数据库。 DataGrip 2023软件获取 DataGrip 2…...

突破编程_C++_设计模式(命令模式)

1 命令模式的基本概念 C 命令模式是一种设计模式&#xff0c;它允许将请求封装为一个对象&#xff0c;从而可以用不同的请求对客户进行参数化&#xff1b;对请求排队或记录请求日志&#xff0c;以及支持可撤销的操作。命令模式的主要目的是将请求封装为对象&#xff0c;从而可…...

LeetCode102题:二叉树的层序遍历(python3)

代码思路&#xff1a;使用队列先进先出的特性&#xff0c;queue[]不为空进入for循环&#xff0c;tmp存储每层的节点&#xff0c;将结果添加至res[]中。 python中使用collections中的双端队列deque()&#xff0c;其popleft()方法可达到O(1)时间复杂度。 class Solution:def lev…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]

报错信息&#xff1a;libc.so.6: cannot open shared object file: No such file or directory&#xff1a; #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...