Quartz是一个广泛使用的开源任务调度框架,用于在Java应用程序中执行定时任务和周期性任务。它提供了强大的调度功能,允许您计划、管理和执行各种任务,从简单的任务到复杂的任务。
以下是Quartz的一些关键特点和功能:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<version>2.5.4</version>
</dependency>
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 AjaxRespData<ScheduleJobVO> addJob(@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<Void> 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<ScheduleJobVO> 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<ScheduleJobVO> 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<ScheduleJobEntity, ScheduleJobData> {
final ScheduleJobRepository scheduleJobRepository;
final QuartzHelper quartzHelper;
@PostConstruct
public void init(){
log.info("init schedule job...");
List<ScheduleJobEntity> 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<ScheduleJobEntity> 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;
}
}
定时任务执行间隔,最低设置一分钟