Quartz是一个广泛使用的开源任务调度框架,用于在Java应用程序中执行定时任务和周期性任务。它提供了强大的调度功能,允许您计划、管理和执行各种任务,从简单的任务到复杂的任务。
以下是Quartz的一些关键特点和功能:
Job)。这允许您选择最适合您需求的作业类型。
org.springframework.boot spring-boot-starter-quartz 2.5.4
spring.quartz.job-store-type=jdbc # The first boot uses ALWAYS spring.quartz.jdbc.initialize-schema=never spring.quartz.auto-startup=true spring.quartz.startup-delay=5s spring.quartz.overwrite-existing-jobs=true spring.quartz.properties.org.quartz.scheduler.instanceName=ClusterQuartz spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO spring.quartz.properties.org.quartz.jobStore.class=org.springframework.scheduling.quartz.LocalDataSourceJobStore spring.quartz.properties.org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate spring.quartz.properties.org.quartz.jobStore.tablePrefix=QRTZ_ spring.quartz.properties.org.quartz.jobStore.isClustered=true spring.quartz.properties.org.quartz.jobStore.acquireTriggersWithinLock=true spring.quartz.properties.org.quartz.jobStore.misfireThreshold=12000 spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval=5000 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
配置解释
spring.quartz.job-store-type=jdbc:指定使用数据库作为Quartz的作业存储。 spring.quartz.jdbc.initialize-schema=never:此属性设置为"never",表示不会自动初始化Quartz数据库架构。这意味着您需要手动创建Quartz数据库表,以便Quartz正常运行。您可以使用Quartz提供的SQL脚本来创建这些表。 spring.quartz.auto-startup=true:设置为true,表示Quartz在Spring Boot应用程序启动时自动启动。 spring.quartz.startup-delay=5s:Quartz启动延迟时间,设置为5秒。 spring.quartz.overwrite-existing-jobs=true:设置为true,表示如果任务已经存在,则覆盖现有的任务。 spring.quartz.properties.org.quartz.scheduler.instanceName=ClusterQuartz:为Quartz调度器设置实例名称。 spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO:Quartz调度器实例ID设置为"AUTO",这意味着它会自动分配一个唯一的实例ID。 spring.quartz.properties.org.quartz.jobStore.class=org.springframework.scheduling.quartz.LocalDataSourceJobStore:指定使用org.springframework.scheduling.quartz.LocalDataSourceJobStore作为作业存储。 spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate:指定使用PostgreSQL数据库的委托类。 spring.quartz.properties.org.quartz.jobStore.tablePrefix=QRTZ_:为Quartz数据库表添加前缀,以避免与其他表冲突。 spring.quartz.properties.org.quartz.jobStore.isClustered=true:启用Quartz集群模式。 spring.quartz.properties.org.quartz.jobStore.acquireTriggersWithinLock=true:设置为true,以确保在获取触发器时使用锁来处理并发。 spring.quartz.properties.org.quartz.jobStore.misfireThreshold=12000:设置Quartz任务的超时时间,以确定任务是否错过执行。 spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval=5000:设置节点之间检查其状态的时间间隔。 spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool:定义Quartz线程池的类型。 spring.quartz.properties.org.quartz.threadPool.threadCount=1:设置线程池中线程的数量。 spring.quartz.properties.org.quartz.threadPool.threadPriority=5:设置线程的优先级。 spring.quartz.properties.org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true:允许线程继承初始化线程的类加载器。
import cn.hutool.extra.spring.SpringUtil; import lombok.extern.slf4j.Slf4j; import org.quartz.DisallowConcurrentExecution; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.PersistJobDataAfterExecution; import org.springframework.stereotype.Component; /** * @author Wang */ @PersistJobDataAfterExecution @DisallowConcurrentExecution @Slf4j @Component public class DealerJob implements Job { @Override public void execute(JobExecutionContext context) { log.info("start Quartz job name: {}", context.getJobDetail().getKey().getName()); DealerImportFacade dealerImportFacade = SpringUtil.getBean(DealerImportFacade.class); log.info(" start import US dealer data "); RequestContext.current().set(RequestContextCons.REGION, DataSourceEnum.US.toString().toLowerCase()); try { // dealerImportFacade.importUsDealerData(); log.info(" end import US dealer data "); } catch (Exception e) { log.error(e.getMessage(), e); } } }
import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; /** * @author Wang */ @RequiredArgsConstructor @Slf4j @RestController @RequestMapping("/schedule/job") public class ScheduleJobController { final ScheduleJobService scheduleJobService; final QuartzHelper quartzHelper; @PostMapping public AjaxRespDataaddJob(@Valid @RequestBody AddScheduleJobDTO scheduleJobDTO) { ScheduleJobEntity scheduleJobEntity = BeanConvertUtils.convert(scheduleJobDTO, ScheduleJobEntity.class); scheduleJobEntity.init(); scheduleJobEntity.setStatus(EnumScheduleJobStatus.RUN); ScheduleJobData data = scheduleJobService.save(scheduleJobEntity); quartzHelper.scheduleJob(data); return AjaxRespData.success(BeanConvertUtils.convert(data, ScheduleJobVO.class)); } @DeleteMapping("/{jobId}") public AjaxRespData removeJob(@PathVariable("jobId") String jobId) { ScheduleJobEntity scheduleJobEntity = scheduleJobService.checkExist(jobId, EnumError.E30001); scheduleJobService.remove(jobId); quartzHelper.remove(scheduleJobEntity); return AjaxRespData.success(); } @PutMapping("/{jobId}") public AjaxRespData updateJob(@PathVariable String jobId, @Valid @RequestBody AddScheduleJobDTO scheduleJobDTO) { ScheduleJobEntity scheduleJobEntity = BeanConvertUtils.convert(scheduleJobDTO, ScheduleJobEntity.class); scheduleJobEntity.setId(jobId); ScheduleJobData data = scheduleJobService.update(scheduleJobEntity); quartzHelper.scheduleJob(data); return AjaxRespData.success(BeanConvertUtils.convert(data, ScheduleJobVO.class)); } @GetMapping("/{jobId}") public AjaxRespData getJob(@PathVariable("jobId") String jobId) { ScheduleJobEntity scheduleJobEntity = scheduleJobService.checkExist(jobId, EnumError.E30001); return AjaxRespData.success(BeanConvertUtils.convert(scheduleJobEntity, ScheduleJobVO.class)); } @PutMapping("/operate") public void operateJob(@Valid @RequestBody AddScheduleJobDTO scheduleJobDTO) { ScheduleJobEntity scheduleJobEntity = scheduleJobService.checkExist(scheduleJobDTO.getId(), EnumError.E30001); scheduleJobEntity.setStatus(scheduleJobDTO.getStatus()); scheduleJobService.update(scheduleJobEntity); quartzHelper.operateJob(scheduleJobDTO.getStatus(), scheduleJobEntity); } }
import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.List; /** * @author Wang */ @RequiredArgsConstructor @Slf4j @Service public class ScheduleJobService extends BaseService{ final ScheduleJobRepository scheduleJobRepository; final QuartzHelper quartzHelper; @PostConstruct public void init(){ log.info("init schedule job..."); List jobs = this.getRepository().findAll(); for (ScheduleJobEntity job : jobs) { quartzHelper.scheduleJob(job); quartzHelper.operateJob(EnumScheduleJobStatus.PAUSE, job); if (job.getStatus().equals(EnumScheduleJobStatus.RUN)) { quartzHelper.operateJob(EnumScheduleJobStatus.RUN, job); } } log.info("init schedule job completed..."); } @Override public BaseRepository getRepository() { return scheduleJobRepository; } }
import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.quartz.*; import org.springframework.stereotype.Component; import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.Objects; /** * @author Wang */ @RequiredArgsConstructor @Slf4j @Component public class QuartzHelper { final Scheduler scheduler; public void scheduleJob(ScheduleJobEntity jobInfo) { JobKey jobKey = JobKey.jobKey(jobInfo.getJobName(), jobInfo.getJobGroup()); try { JobDetail jobDetail = scheduler.getJobDetail(jobKey); if (Objects.nonNull(jobDetail)){ scheduler.deleteJob(jobKey); } } catch (SchedulerException e) { e.printStackTrace(); } JobDetail jobDetail = JobBuilder.newJob(getJobClass(jobInfo.getType())) .withIdentity(jobKey) .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity(jobInfo.getTriggerName(), jobInfo.getTriggerGroup()).startNow() .withSchedule(CronScheduleBuilder.cronSchedule(jobInfo.getCronExpression())) .build(); try { scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException e) { log.error(e.getMessage(), e); } } public void rescheduleJob(ScheduleJobEntity job) { TriggerKey triggerKey = new TriggerKey(job.getTriggerName(), job.getTriggerGroup()); try { CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey); CronTrigger newCronTrigger = cronTrigger.getTriggerBuilder() .withIdentity(triggerKey) .withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression())) .build(); scheduler.rescheduleJob(triggerKey, newCronTrigger); } catch (SchedulerException e) { throw new RuntimeException(e); } } public void remove(ScheduleJobEntity job) { TriggerKey triggerKey = new TriggerKey(job.getTriggerName(), job.getTriggerGroup()); try { scheduler.pauseTrigger(triggerKey); scheduler.unscheduleJob(triggerKey); scheduler.deleteJob(JobKey.jobKey(job.getTriggerName(), job.getTriggerGroup())); } catch (SchedulerException e) { throw new RuntimeException(e); } } public void unscheduleJob(ScheduleJobEntity job) { TriggerKey triggerKey = new TriggerKey(job.getTriggerName(), job.getTriggerGroup()); try { scheduler.unscheduleJob(triggerKey); } catch (SchedulerException e) { throw new RuntimeException(e); } } public void operateJob(EnumScheduleJobStatus status, ScheduleJobEntity job) { JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup()); try { switch (status) { case RUN: scheduler.resumeJob(jobKey); break; case PAUSE: scheduler.pauseJob(jobKey); break; default: throw new IllegalArgumentException(); } } catch (SchedulerException e) { throw new RuntimeException(e); } } public String nextTime(ScheduleJobEntity job) { TriggerKey triggerKey = new TriggerKey(job.getTriggerName(), job.getTriggerGroup()); try { Trigger trigger = scheduler.getTrigger(triggerKey); Date nextFireTime = trigger.getNextFireTime(); return DateUtil.format(nextFireTime, DateTimeFormatter.ISO_DATE_TIME); } catch (SchedulerException e) { throw new RuntimeException(e); } } private Class extends Job> getJobClass(EnumScheduleJobType type) { Class extends Job> clazz; switch (type) { case DEALER_IMPORT: clazz = DealerJob.class; break; // case SECONDARY_INVITING_EXPIRE: // clazz = MockDeviceReportJob.class; // break; default: throw new IllegalArgumentException(); } return clazz; } }
实例1,8281
实例2,8282
定时任务执行间隔,最低设置一分钟