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");}
}

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

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

相关文章

YOLOv9改进 添加新型卷积注意力框架SegNext_Attention

一、SegNext论文 论文地址:2209.08575.pdf (arxiv.org) 二、 SegNext_Attention注意力框架结构 在SegNext_Attention中,注意力机制被引入到编码器和解码器之间的连接中,帮助模型更好地利用全局上下文信息。具体而言,注意力机制通过学习像素级的注意力权重,使得模型可以对…

深度解析react中hooks的底层原理是啥?React架构原理深度解析

1.React Hooks 底层原理 React Hooks 的底层原理是基于 React Fiber 架构的实现。下面是对 React Hooks 底层原理的深度解析: Fiber 架构: React Fiber 是 React 的新的协调引擎,它的设计目标是支持增量式更新、优先级调度、暂停和继续执行等特性。Fiber 架构重新实现了 Re…

FFmpeg开发笔记(十)Linux环境给FFmpeg集成vorbis和amr

FFmpeg内置了aac音频格式&#xff0c;在《FFmpeg开发实战&#xff1a;从零基础到短视频上线》一书的“5.2.2 Linux环境集成mp3lame”又介绍了如何给FFmpeg集成mp3格式&#xff0c;常见的音频文件除了这两种之外&#xff0c;还有ogg和amr两种格式也较常用。其中ogg格式的编解码…

【C++庖丁解牛】vector容器的简易模拟实现(C++实现)(最后附源码)

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 前言vector容器代码实现内…

前端JavaScript篇之常见事件

目录 JavaScript常见事件click&#xff08;点击&#xff09;mouseover&#xff08;鼠标悬停&#xff09;keydown&#xff08;按键按下&#xff09;load&#xff08;加载&#xff09;submit&#xff08;提交&#xff09; JavaScript常见事件 JavaScript中的事件是指用户与网页元…

python基础13_字符编码

什么是字符编码呢? 在讲字符编码的之前,我先讲一个东西叫字符集 什么是字符集呢? python默认的字符集unicode 很简单,就是他把大部分国家的文字,符号,都集合过来,每一个文字或者说是符号都有一个编号去代表 你可以理解成高考 每个考生的准考证号,就代表这个学生 那么字…

JVM垃圾收集器-serial.parNew,parallelScavnge,serialOld,parallelOld,CMS,G1

垃圾收集器 分代模型 适用于新生代&#xff1a; serial parNew parallel Scaavenge 适用于老年代&#xff1a; CMS serial Old(msc) paraller Old 分区模型 适用于超大容量&#xff1a; G1 分代模型 serial /serial Old收集器 1.单线程收集器 2.收集时会暂停其他线程&…

剑指offer C ++双栈实现队列

1. 基础 队列&#xff1a;先进先出&#xff0c;即插入数据在队尾进行&#xff0c;删除数据在队头进行&#xff1b; 栈&#xff1a;后进先出&#xff0c;即插入与删除数据均在栈顶进行。 2. 思路 两个栈实现一个队列的思想&#xff1a;用pushStack栈作为push数据的栈&#xff…

使用canvas绘制超炫时钟

HTML5 Canvas相当于一个画板&#xff0c;你可以在Canvas绘制任意的东西&#xff0c;今天要分享一款利用HTML5 Canvas绘制的超炫时钟的方法及代码&#xff0c;非常的漂亮&#xff0c;这里推荐给大家 代码地址 使用canvas绘制超炫时钟

react中JSX的详解

目录 JSX的本质及其与JavaScript的关系探究 一、JSX的本质 二、JSX与JavaScript的关系 三、为什么要使用JSX 四、不使用JSX的后果 五、JSX背后的功能模块 JSX的本质及其与JavaScript的关系探究 在React开发中&#xff0c;JSX是一个不可或缺的部分。那么&#xff0c;JSX的…

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

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

5.62 BCC工具之cpudist.py解读

一,工具简介 cpudist会将任务在CPU上的时间汇总为直方图,显示任务在被取消调度之前在CPU上花费的时间。这提供了有价值的信息,可以指示过度订阅(处理器太少而任务太多)、由于过多上下文切换而产生的开销(例如,多个线程共用的常见锁)、工作负载分布不均、任务过于细化等…

数据结构 第1章:绪论

文章目录 1. 绪论1.1. 数据结构 1.2. 算法1.2.1. 算法的基本概念1.2.2. 算法的时间复杂度1.2.3. 算法的空间复杂度 1. 绪论 程序 数据结构 算法 1.1. 数据结构 数据&#xff1a;是对客观事物的符号表示&#xff0c;在计算机科学中是指所有能输入到计算机中并被计算机程序处理…

【机器学习300问】33、决策树是如何进行特征选择的?

还记得我在【机器学习300问】的第28问里谈到的&#xff0c;看决策树的定义不就是if-else语句吗怎么被称为机器学习模型&#xff1f;其中最重要的两点就是决策树算法要能够自己回答下面两问题&#xff1a; 该选哪些特征 特征选择该选哪个阈值 阈值确定 今天这篇文章承接上文&…

神经网络中的先验知识

单调性约束&#xff1a; 单调性约束对于某些问题非常关键&#xff0c;可以确保模型输出与输入的关系保持单调。在我的实践中&#xff0c;我发现通过引入这种约束&#xff0c;能够有效提高模型的性能&#xff0c;特别是在需要保持某些顺序关系的任务上。 平滑性约束&#xff1a…

探秘高校线上心理咨询室:Java+SpringBoot的实践

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

蓝桥·算法双周赛|第七场分级赛——小白入门赛

&#x1f525;博客介绍&#xff1a; 27dCnc &#x1f3a5;系列专栏&#xff1a; <<数据结构与算法>> << 算法入门>> << C项目>> &#x1f3a5; 当前专栏: << 算法入门>> 专题 : 数据结构帮助小白快速入门算法 &#x1f4…

解锁Redis宝藏:探索Redis哈希(Hash)的神奇世界!

Redis 哈希(Hash)是一种灵活且功能强大的数据结构&#xff0c;它可以存储多个字段和对应的值&#xff0c;适用于存储对象。了解 Redis 哈希的基本命令、示例和使用方法&#xff0c;以及常见的应用场景&#xff0c;将帮助您更好地利用 Redis 的强大功能。 Redis 哈希的基本命令…

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

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

C#四部曲(知识补充)

Unity跨平台原理 .Net相关 只要编写的时候遵循.NET的这些规则&#xff0c;就能在.NET平台下通用 各种源码→根据.NET规范编写→(虚拟机)生成CIL中间码(保存在程序集中)→转成操作系统原代码 跨语言← 跨平台↓ Unity跨平台原理&#xff08;Mono&#xff09; c#脚本→MonoC#编…