1、需求
配置中,固定周期,单位秒。需要任务每间隔这个秒数 执行进行统计。
2、分析
要实现这个需求,之前一直在用的多线程方案也行。详见
既然前面用quartz 根据cron表达式上一次和下一次的执行时间判断。
本次就用quartz来实现动态任务。
毫无疑问,quartz更专业,功能更强大。支持事务,支持任务持久化。事务这边不需要。持久化看产品需求了。
3、编码实现
3.1 QuartzSchedulerConfig
@Configuration
public class QuartzSchedulerConfig {
@Bean
public SchedulerFactoryBean schedulerFactory() {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setBeanName("rules-scheduler");
factory.setOverwriteExistingJobs(true);
factory.setAutoStartup(true);
return factory;
}
}
3.2 FixedCycleSchedule
动态实现新增和删除 - 与数据库记录匹配
public class FixedCycleSchedule {
private static final String GROUP = "fixed";
@Autowired
SchedulerFactoryBean schedulerFactoryBean;
@Scheduled(fixedRate = 60 * 1000)
private void configureTasks() {
log.info("fixed cycle schedule single round start");
Scheduler scheduler = schedulerFactoryBean.getScheduler();
Map<String,CustomData> map = DbService.getFixedCycle().stream().collect(Collectors.toMap(CustomData::getId, Function.identity()));
try {
List<String> existingList = new ArrayList<>(16);
for (TriggerKey triggerKey : scheduler.getTriggerKeys(GroupMatcher.groupEquals(GROUP))) {
String taskName = triggerKey.getName();
if (!map.containsKey(taskName)) {
System.out.println("remove " + taskName);
scheduler.unscheduleJob(triggerKey);
scheduler.deleteJob(JobKey.jobKey(taskName, GROUP));
continue;
}
existingList.add(taskName);
}
List<CustomData> adds = new ArrayList<>(16);
for (String s : map.keySet()) {
if(!existingList.contains(s)) {
adds.add(map.get(s));
}
}
if(!adds.isEmpty()) {
newTasks(scheduler,adds);
}
} catch (Exception e) {
log.error(e.getMessage());
}
log.info("fixed cycle schedule single round end");
}
private void newTasks(Scheduler scheduler, List<CustomData> adds) throws Exception{
for (CustomData customData : adds) {
JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
.withIdentity(customData.getId(), GROUP)
.build();
SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(customData.getCycle())
.repeatForever();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(customData.getId(), GROUP)
.startNow()
.withSchedule(scheduleBuilder)
.build();
System.out.println("newTasks " + customData.getId());
scheduler.scheduleJob(jobDetail, trigger);
}
}
}
用spring的schedule每一分钟同步一次。
3.3 Job
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 获取 Trigger
Trigger trigger = context.getTrigger();
TriggerKey key = trigger.getKey();
// 获取 Scheduler
Scheduler scheduler = context.getScheduler();
System.out.println("context.getJobDetail().getKey().getName() = " + key.getName());
System.out.println("Job executed at " + new Date());
SimpleTrigger t = (SimpleTrigger)trigger;
System.out.println("t.getRepeatInterval() = " + t.getRepeatInterval());
try {
if(queryDatabaseForNewSecond == 1) {
scheduler.pauseTrigger(key);
scheduler.unscheduleJob(key);
}
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
queryDatabaseForNewSecond==1 可以用来与库中对比,周期配置如有变更,那么需要更新。一开始打算在job中直接更新,更新也是需要停掉,再newScheduleBuilder、newTrigger,再启 。那么直接停掉。外面的FixedCycleSchedule也会再新建
4、结语
Quartz 方式
优点
-
高级调度能力:
- Quartz 提供了丰富的调度功能,如固定间隔、固定频率、基于日历的调度等。
- 可以轻松配置复杂的调度策略,如在特定日期和时间执行任务。
-
任务管理:
- 支持任务的暂停、恢复、取消等功能。
- 可以查看任务的状态,如是否正在执行、何时执行等。
-
持久化支持:
- Quartz 可以将调度信息持久化到数据库中,这样即使应用程序重启,调度也不会丢失。
- 支持集群部署,可以在多个节点之间共享调度信息。
-
灵活性:
- 支持多种类型的触发器,如
SimpleTrigger
和CronTrigger
。 - 可以配置多个触发器来调度同一个作业。
- 支持多种类型的触发器,如
-
健壮性:
- Quartz 在设计时考虑了高可用性和容错性。
- 支持故障转移和恢复,可以在任务失败时自动重试。
多线程方式
优点
-
简单易用:
- Java 提供了强大的多线程支持,如
Thread
和Runnable
接口。 - 可以轻松创建线程并控制线程的生命周期。
- Java 提供了强大的多线程支持,如
-
轻量级:
- 相对于 Quartz,多线程模型更为轻量级。
- 不需要额外的配置或持久化支持。
-
灵活的任务执行:
- 可以根据需要自由控制线程的启动和停止。
- 可以使用
ExecutorService
来管理线程池,提高资源利用率。
择优而用!