宝宝快睡觉游戏
85.67M · 2025-12-13
Quartz 在正确配置集群的情况下,不会出现 Spring Task 那样的重复执行问题。 核心区别在于:
因此,在微服务架构和集群部署场景下,Quartz 是更可靠的定时任务解决方案。
Quartz 是一个功能强大、开源的任务调度框架,可以用来创建简单或复杂的定时任务调度方案。
调度器,任务调度的核心控制器
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
任务接口,定义要执行的工作
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) {
// 任务执行逻辑
}
}
触发器,定义任务执行的时间规则
任务详情,包含任务的详细信息
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
public class QuartzDemo {
public static void main(String[] args) throws SchedulerException {
// 1. 创建调度器
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 2. 启动调度器
scheduler.start();
// 3. 创建任务
JobDetail job = JobBuilder.newJob(MyJob.class)
.withIdentity("myJob", "group1")
.build();
// 4. 创建触发器
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(10)
.repeatForever())
.build();
// 5. 将任务和触发器注册到调度器
scheduler.scheduleJob(job, trigger);
}
}
public class DataProcessJob implements Job {
@Override
public void execute(JobExecutionContext context) {
// 获取JobDetail中的数据
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String jobName = dataMap.getString("jobName");
// 获取Trigger中的数据
JobDataMap triggerDataMap = context.getTrigger().getJobDataMap();
String triggerName = triggerDataMap.getString("triggerName");
// 执行任务逻辑
System.out.println("执行任务: " + jobName);
}
}
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class StatefulJob implements Job {
@Override
public void execute(JobExecutionContext context) {
// 这个Job不会并发执行,且会持久化JobDataMap
}
}
// 立即开始,每10秒执行一次,总共执行5次
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(10)
.withRepeatCount(4)) // 重复4次,总共5次
.build();
// 指定开始时间,每天执行
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger2", "group1")
.startAt(DateBuilder.todayAt(10, 0, 0)) // 今天10:00开始
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInHours(24)
.repeatForever())
.build();
// 使用Cron表达式
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 12 * * ?")) // 每天12:00执行
.build();
// 常用的Cron表达式示例
// "0 0/5 * * * ?" 每5分钟执行一次
// "0 0 9-17 * * ?" 朝九晚五内每小时执行一次
// "0 0 12 ? * WED" 每周三12:00执行
// "0 15 10 ? * MON-FRI" 周一到周五10:15执行
public class DataProcessJob implements Job {
@Override
public void execute(JobExecutionContext context) {
// 获取JobDataMap中的数据
JobDataMap dataMap = context.getMergedJobDataMap();
String dataSource = dataMap.getString("dataSource");
int batchSize = dataMap.getInt("batchSize");
processData(dataSource, batchSize);
}
private void processData(String dataSource, int batchSize) {
// 数据处理逻辑
}
}
// 创建Job时设置数据
JobDetail job = JobBuilder.newJob(DataProcessJob.class)
.withIdentity("dataJob", "group1")
.usingJobData("dataSource", "mysql")
.usingJobData("batchSize", 1000)
.build();
// 创建Trigger时也可以设置数据
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("dataTrigger", "group1")
.usingJobData("triggerName", "dailyTrigger")
.withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(14, 30))
.build();
public class CustomJobListener implements JobListener {
@Override
public String getName() {
return "CustomJobListener";
}
@Override
public void jobToBeExecuted(JobExecutionContext context) {
System.out.println("Job即将执行: " + context.getJobDetail().getKey());
}
@Override
public void jobExecutionVetoed(JobExecutionContext context) {
System.out.println("Job执行被否决: " + context.getJobDetail().getKey());
}
@Override
public void jobWasExecuted(JobExecutionContext context,
JobExecutionException jobException) {
System.out.println("Job执行完成: " + context.getJobDetail().getKey());
}
}
public class CustomTriggerListener implements TriggerListener {
@Override
public String getName() {
return "CustomTriggerListener";
}
@Override
public void triggerFired(Trigger trigger, JobExecutionContext context) {
System.out.println("Trigger触发: " + trigger.getKey());
}
@Override
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
// 返回true表示否决Job执行
return false;
}
@Override
public void triggerMisfired(Trigger trigger) {
System.out.println("Trigger错过触发: " + trigger.getKey());
}
@Override
public void triggerComplete(Trigger trigger, JobExecutionContext context,
Trigger.CompletedExecutionInstruction triggerInstructionCode) {
System.out.println("Trigger执行完成: " + trigger.getKey());
}
}
scheduler.getListenerManager().addJobListener(new CustomJobListener());
scheduler.getListenerManager().addTriggerListener(new CustomTriggerListener());
# 配置数据源
org.quartz.dataSource.myDS.driver = com.mysql.cj.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = password
org.quartz.dataSource.myDS.maxConnections = 10
# 使用JDBC JobStore
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = myDS
# 集群配置
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000
<bean id="schedulerFactoryBean"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="simpleTrigger"/>
<ref bean="cronTrigger"/>
</list>
</property>
<property name="jobDetails">
<list>
<ref bean="jobDetail"/>
</list>
</property>
</bean>
<bean id="jobDetail"
class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.example.MyJob"/>
<property name="jobDataAsMap">
<map>
<entry key="timeout" value="5"/>
</map>
</property>
</bean>
<bean id="simpleTrigger"
class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="jobDetail"/>
<property name="repeatInterval" value="10000"/>
<property name="startDelay" value="1000"/>
</bean>
@Configuration
public class QuartzConfig {
@Bean
public JobDetail sampleJobDetail() {
return JobBuilder.newJob(SampleJob.class)
.withIdentity("sampleJob")
.storeDurably()
.build();
}
@Bean
public Trigger sampleJobTrigger() {
SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(10)
.repeatForever();
return TriggerBuilder.newTrigger()
.forJob(sampleJobDetail())
.withIdentity("sampleTrigger")
.withSchedule(scheduleBuilder)
.build();
}
}
public class RobustJob implements Job {
@Override
public void execute(JobExecutionContext context) {
try {
// 业务逻辑
doBusiness();
} catch (Exception e) {
// 记录异常但不抛出,避免任务被停止
log.error("Job执行异常", e);
// 如果需要重试,可以重新调度
if (needRetry()) {
rescheduleJob(context);
}
}
}
private void rescheduleJob(JobExecutionContext context) {
try {
Scheduler scheduler = context.getScheduler();
Trigger oldTrigger = context.getTrigger();
Trigger newTrigger = oldTrigger.getTriggerBuilder()
.startAt(new Date(System.currentTimeMillis() + 60000)) // 1分钟后重试
.build();
scheduler.rescheduleJob(oldTrigger.getKey(), newTrigger);
} catch (SchedulerException e) {
log.error("重新调度任务失败", e);
}
}
}
// 使用@DisallowConcurrentExecution避免并发
@DisallowConcurrentExecution
public class ExpensiveJob implements Job {
// 耗时任务,避免并发执行
}
// 使用@PersistJobDataAfterExecution持久化状态
@PersistJobDataAfterExecution
public class StatefulDataJob implements Job {
// 需要保持状态的Job
}
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")
.withMisfireHandlingInstructionFireAndProceed()) // 错过处理策略
.build();
// 常用的错过处理策略:
// withMisfireHandlingInstructionIgnoreMisfires() - 忽略并立即执行
// withMisfireHandlingInstructionFireAndProceed() - 立即执行一次,然后按计划
// withMisfireHandlingInstructionDoNothing() - 什么都不做
在集群环境中,确保:
Quartz 是一个功能完整的任务调度框架,通过合理配置可以满足各种复杂的调度需求。在实际使用中,建议: