1、需求
配置中,固定周期,单位秒。需要任务每间隔这个秒数 执行进行统计。
2、分析
要实现这个需求,之前一直在用的多线程方案也行。详见
既然前面用quartz 根据cron表达式上一次和下一次的执行时间判断。
本次就用quartz来实现动态任务。
毫无疑问,quartz更专业,功能更强大。支持事务,支持任务持久化。事务这边不需要。持久化看产品需求了。
3、编码实现
3.1 QuartzSchedulerConfig
@Configuration
public class QuartzSchedulerConfig {@Beanpublic 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";@AutowiredSchedulerFactoryBean 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 {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {// 获取 TriggerTrigger trigger = context.getTrigger();TriggerKey key = trigger.getKey();// 获取 SchedulerScheduler 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
来管理线程池,提高资源利用率。
择优而用!