quartz实现动态定时任务管理

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 方式

优点
  1. 高级调度能力

    • Quartz 提供了丰富的调度功能,如固定间隔、固定频率、基于日历的调度等。
    • 可以轻松配置复杂的调度策略,如在特定日期和时间执行任务。
  2. 任务管理

    • 支持任务的暂停、恢复、取消等功能。
    • 可以查看任务的状态,如是否正在执行、何时执行等。
  3. 持久化支持

    • Quartz 可以将调度信息持久化到数据库中,这样即使应用程序重启,调度也不会丢失。
    • 支持集群部署,可以在多个节点之间共享调度信息。
  4. 灵活性

    • 支持多种类型的触发器,如 SimpleTrigger 和 CronTrigger
    • 可以配置多个触发器来调度同一个作业。
  5. 健壮性

    • Quartz 在设计时考虑了高可用性和容错性。
    • 支持故障转移和恢复,可以在任务失败时自动重试。

多线程方式

优点
  1. 简单易用

    • Java 提供了强大的多线程支持,如 Thread 和 Runnable 接口。
    • 可以轻松创建线程并控制线程的生命周期。
  2. 轻量级

    • 相对于 Quartz,多线程模型更为轻量级。
    • 不需要额外的配置或持久化支持。
  3. 灵活的任务执行

    • 可以根据需要自由控制线程的启动和停止。
    • 可以使用 ExecutorService 来管理线程池,提高资源利用率。

择优而用!

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是 SpringBoot 搭配 Quartz 实现动态定时任务的源码: 1. 首先,我们需要引入 Quartz 和 SpringBoot 的依赖: ```xml <!-- Quartz相关依赖 --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.3.2</version> </dependency> <!-- SpringBoot相关依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> ``` 2. 创建定时任务实体类,用于封装定时任务的信息,包括任务名称、任务组、任务类名、任务状态(是否启用)、任务表达式等: ```java @Entity @Table(name = "job_task") @Data public class JobTask implements Serializable { private static final long serialVersionUID = 1L; /** * ID */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; /** * 任务名称 */ @NotBlank(message = "任务名称不能为空") private String name; /** * 任务分组 */ @NotBlank(message = "任务分组不能为空") private String group; /** * 任务类名 */ @NotBlank(message = "任务类名不能为空") private String className; /** * 任务状态,0:禁用,1:启用 */ @NotNull(message = "任务状态不能为空") private Integer status; /** * 任务表达式 */ @NotBlank(message = "任务表达式不能为空") private String cronExpression; /** * 创建时间 */ private LocalDateTime createTime; /** * 最后一次修改时间 */ private LocalDateTime updateTime; } ``` 3. 创建定时任务的服务类,用于管理定时任务的增删改查等操作,同时也需要实现 `InitializingBean` 接口,在启动应用时加载已存在的定时任务: ```java @Service @AllArgsConstructor public class JobTaskService implements InitializingBean { private final Scheduler scheduler; private final JobTaskRepository jobTaskRepository; /** * 添加任务 * @param jobTask * @return * @throws Exception */ public boolean addJobTask(JobTask jobTask) throws Exception { if (jobTask == null || StringUtils.isBlank(jobTask.getCronExpression())) { return false; } if (StringUtils.isBlank(jobTask.getName()) || StringUtils.isBlank(jobTask.getClassName())) { throw new Exception("任务名称或任务类名不能为空"); } // 判断任务是否已存在 JobKey jobKey = JobKey.jobKey(jobTask.getName(), jobTask.getGroup()); if (scheduler.checkExists(jobKey)) { return false; } // 构建任务实例 JobDetail jobDetail = JobBuilder.newJob(getClass(jobTask.getClassName()).getClass()) .withIdentity(jobTask.getName(), jobTask.getGroup()) .build(); // 构建任务触发器 CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(jobTask.getCronExpression()); CronTrigger trigger = TriggerBuilder.newTrigger() .withIdentity(jobTask.getName(), jobTask.getGroup()) .withSchedule(cronScheduleBuilder) .build(); // 注册任务和触发器 scheduler.scheduleJob(jobDetail, trigger); // 如果任务状态为启用,则立即启动任务 if (jobTask.getStatus() == 1) { scheduler.triggerJob(jobKey); } // 保存任务信息 jobTask.setCreateTime(LocalDateTime.now()); jobTask.setUpdateTime(LocalDateTime.now()); jobTaskRepository.save(jobTask); return true; } /** * 修改任务 * @param jobTask * @return * @throws Exception */ public boolean modifyJobTask(JobTask jobTask) throws Exception { if (jobTask == null || StringUtils.isBlank(jobTask.getCronExpression())) { return false; } if (StringUtils.isBlank(jobTask.getName()) || StringUtils.isBlank(jobTask.getClassName())) { throw new Exception("任务名称或任务类名不能为空"); } // 判断任务是否存在 JobKey jobKey = JobKey.jobKey(jobTask.getName(), jobTask.getGroup()); if (!scheduler.checkExists(jobKey)) { return false; } // 修改任务触发器 CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(jobTask.getCronExpression()); CronTrigger newTrigger = TriggerBuilder.newTrigger() .withIdentity(jobTask.getName(), jobTask.getGroup()) .withSchedule(cronScheduleBuilder) .build(); scheduler.rescheduleJob(TriggerKey.triggerKey(jobTask.getName(), jobTask.getGroup()), newTrigger); // 修改任务信息 JobTask oldJobTask = jobTaskRepository.findByNameAndGroup(jobTask.getName(), jobTask.getGroup()); oldJobTask.setClassName(jobTask.getClassName()); oldJobTask.setStatus(jobTask.getStatus()); oldJobTask.setCronExpression(jobTask.getCronExpression()); oldJobTask.setUpdateTime(LocalDateTime.now()); jobTaskRepository.save(oldJobTask); return true; } /** * 删除任务 * @param name * @param group * @return * @throws Exception */ public boolean deleteJobTask(String name, String group) throws Exception { JobKey jobKey = JobKey.jobKey(name, group); if (!scheduler.checkExists(jobKey)) { return false; } scheduler.deleteJob(jobKey); jobTaskRepository.deleteByNameAndGroup(name, group); return true; } /** * 获取所有任务 * @return */ public List<JobTask> getAllJobTask() { return jobTaskRepository.findAll(); } /** * 根据任务名称和分组获取任务信息 * @param name * @param group * @return */ public JobTask getJobTaskByNameAndGroup(String name, String group) { return jobTaskRepository.findByNameAndGroup(name, group); } /** * 获取任务类实例 * @param className * @return * @throws Exception */ private Object getClass(String className) throws Exception { Class<?> clazz = Class.forName(className); return clazz.newInstance(); } /** * 实现 InitializingBean 接口,在启动应用时加载已存在的定时任务 * @throws Exception */ @Override public void afterPropertiesSet() throws Exception { List<JobTask> jobTaskList = jobTaskRepository.findAll(); for (JobTask jobTask : jobTaskList) { if (jobTask.getStatus() == 1) { addJobTask(jobTask); } } } } ``` 4. 创建定时任务的控制器类,用于处理新增、修改、删除等请求: ```java @RestController @AllArgsConstructor @RequestMapping("/job") public class JobTaskController { private final JobTaskService jobTaskService; /** * 添加任务 * @param jobTask * @return * @throws Exception */ @PostMapping public ResponseEntity addJobTask(@RequestBody JobTask jobTask) throws Exception { boolean result = jobTaskService.addJobTask(jobTask); return result ? ResponseEntity.ok("任务添加成功") : ResponseEntity.badRequest().body("任务添加失败"); } /** * 修改任务 * @param jobTask * @return * @throws Exception */ @PutMapping public ResponseEntity modifyJobTask(@RequestBody JobTask jobTask) throws Exception { boolean result = jobTaskService.modifyJobTask(jobTask); return result ? ResponseEntity.ok("任务修改成功") : ResponseEntity.badRequest().body("任务修改失败"); } /** * 删除任务 * @param name * @param group * @return * @throws Exception */ @DeleteMapping("/{name}/{group}") public ResponseEntity deleteJobTask(@PathVariable String name, @PathVariable String group) throws Exception { boolean result = jobTaskService.deleteJobTask(name, group); return result ? ResponseEntity.ok("任务删除成功") : ResponseEntity.badRequest().body("任务删除失败"); } /** * 获取所有任务 * @return */ @GetMapping public ResponseEntity getAllJobTask() { List<JobTask> jobTaskList = jobTaskService.getAllJobTask(); return ResponseEntity.ok(jobTaskList); } /** * 根据任务名称和分组获取任务信息 * @param name * @param group * @return */ @GetMapping("/{name}/{group}") public ResponseEntity getJobTaskByNameAndGroup(@PathVariable String name, @PathVariable String group) { JobTask jobTask = jobTaskService.getJobTaskByNameAndGroup(name, group); return ResponseEntity.ok(jobTask); } } ``` 5. 创建定时任务的启动类,用于启动 SpringBoot 应用: ```java @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } /** * 注册定时任务调度器 * @return * @throws SchedulerException */ @Bean public SchedulerFactoryBean schedulerFactoryBean() throws SchedulerException { SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); Properties properties = new Properties(); properties.put("org.quartz.scheduler.instanceName", "ChitGPTScheduler"); properties.put("org.quartz.threadPool.threadCount", "10"); schedulerFactoryBean.setQuartzProperties(properties); schedulerFactoryBean.setStartupDelay(5); return schedulerFactoryBean; } /** * 注册定时任务实例 * @return */ @Bean public Scheduler scheduler() { return schedulerFactoryBean().getScheduler(); } } ``` 以上就是 SpringBoot 搭配 Quartz 实现动态定时任务的源码,希望能对您有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值