springboot调度框架的使用-有效管理定时任务

6 篇文章 0 订阅
2 篇文章 0 订阅

背景:定时任务其实已经用过多套代码了,比如quartz或者是直接注解的@Scheduled,还有之前CronTrigger加载cron函数来配合库表进行操作的定时任务管理。但是这些总少了一些调度的灵活性,刚好本次也要做一个调度框架,那对之前用过的在完善下,意在使用更加灵活方便。

框架:springboot+mybatisplus组合

直接开始完整步骤----->

1.启动类加上支持定时任务注解

 2.建表以及实体类

CREATE TABLE `index_schedule` (
  `id` BIGINT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(64) DEFAULT NULL COMMENT '调度名称',
  `active` TINYINT DEFAULT '1' COMMENT '是否启动   0否  1是',
  `cron_expression` VARCHAR(100) DEFAULT NULL COMMENT 'cron表达式',
  `class_name` VARCHAR(256)  DEFAULT NULL COMMENT '类名(路径)',
  `method_name` VARCHAR(128) DEFAULT NULL COMMENT '方法名',
  `remark` VARCHAR(256) DEFAULT NULL,
  `create_time` DATETIME DEFAULT NULL,
  `create_by` VARCHAR(32) DEFAULT NULL,
  `update_by` VARCHAR(32) DEFAULT NULL,
  `update_time` DATETIME DEFAULT NULL,
  `status` TINYINT DEFAULT '1',
  `del_flag` VARCHAR(2) DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=INNODB comment '调度表'

关键字段说明:

1)class_name类名、method_name方法名:某个类中的方法作为定时任务来调度执行,类名为绝对路径,方法名则为单名称。

2)cron_expression(cron函数):定时任务最基本使用的时间表达式,自行了解。

3)active:定时任务是否启动字段,通过该字段来控制定时任务是否使用

 实体类:略

3.调度框架主要配置类


@Slf4j
@Configuration
public class ScheduledConfig implements SchedulingConfigurer {

    @Resource
    private TaskScheduler taskScheduler;

     //调度任务注册器
    private ScheduledTaskRegistrar taskRegistrar;
    // 调度任务
    private final ConcurrentHashMap<Long, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<>(10);
    // 线程任务
    private final ConcurrentHashMap<Long, ScheduleTask> tasks = new ConcurrentHashMap<>(10);

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskScheduler);
        this.taskRegistrar = taskRegistrar;
    }


    // 1.刷新任务
    public void refreshTask(ScheduleTask cronTask) {
        Long id = cronTask.getId();
        if (cronTask.getActive() == CommonConstrant.INTEGER_ZREO) {
            // 删除任务
            if (scheduledFutures.containsKey(cronTask.getId())) {
                scheduledFutures.get(id).cancel(false);
                scheduledFutures.remove(id);
                tasks.remove(id);
            }
            return;
        }
        // 调度任务已存在且无变化
        if (scheduledFutures.containsKey(id) && StringUtils.equals(tasks.get(id).getCronExpression(),cronTask.getCronExpression())) && 
StringUtils.equals(tasks.get(id).getClassName(),cronTask.getClassName()) &&
StringUtils.equals(tasks.get(id).getMethodName(),cronTask.getMethodName())
       )
        {
            return;
        }

        // 任务发生了变化
        if (scheduledFutures.containsKey(id) && (StringUtils.equals(tasks.get(id).getCronExpression(),cronTask.getCronExpression())) ||
StringUtils.equals(tasks.get(id).getClassName(),cronTask.getClassName()) ||
StringUtils.equals(tasks.get(id).getMethodName(),cronTask.getMethodName()))
)
        {
            scheduledFutures.get(id).cancel(false);
            scheduledFutures.remove(id);
            tasks.remove(id);
        }
        // 添加任务
        Trigger trigger = getTrigger(cronTask);
        taskRegistrar.setScheduler(taskScheduler);
        TaskScheduler scheduler = taskRegistrar.getScheduler();
        ScheduledFuture schedule = scheduler.schedule(getRunable(cronTask),trigger);
        scheduledFutures.put(id,schedule);
        tasks.put(id, cronTask);
        log.info("成功刷新任务--->");
    }


    // 2.设置定时器
    private Trigger getTrigger(ScheduleTask cronTask) {
        if (cronTask.getCronExpression().equals(CommonConstrant.ACTIVE_ONCE)){
            return null;
        }
        return new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                CronTrigger trigger = new CronTrigger(cronTask.getCronExpression());
                Date date = trigger.nextExecutionTime(triggerContext);
                return date;
            }
        };

    }

    /**
     * 3.执行任务(异步且带检测步骤)
     * @param cronTask
     */
    private Runnable getRunable(ScheduleTask cronTask) {
        return new Runnable() {
            /**
             * 验证一条定时任务
             * @param cronTask
             * @return
             */
            private boolean checkOneTask(ScheduleTask cronTask) {
                if (cronTask.getActive()==0) {
                    log.error(cronTask.getClassName() + "的" + cronTask.getMethodName() + ": 该项任务已停止运行");
                    return false;
                }
                Class clazz;
                try {
                    // 1.测试加载类
                    clazz = Class.forName(cronTask.getClassName());
                    Object bean = SpringContextUtils.getBean(clazz);

                    // 2.测试加载方法
                    if (!StringUtils.equals(CommonConstrant.CRON_TASK_METHOD_RUNSCHEDULE,cronTask.getMethodName())){//不带参数
                        bean.getClass().getMethod(cronTask.getMethodName());
                    }else{// 带参数
                        Long[] paramsNum = new Long[]{cronTask.getId()};

                        Class[] classes = new Class[paramsNum.length];
                        for(int i = 0; i < paramsNum.length;i++){
                            classes[i] = Long.class;
                        }
                        bean.getClass().getMethod(cronTask.getMethodName(),classes);
                    }
                    String cronExpression = cronTask.getCronExpression();
                    if (StringUtils.isBlank(cronExpression)) {
                        log.error("该方法未添加定时表达式");
                        return false;
                    }
                } catch (ClassNotFoundException | NoSuchMethodException e) {
                    e.printStackTrace();
                    log.error("找不到类名或方法名", e.getMessage());
                }
                return true;
            }

            @Override
            public void run() {

                // 1.验证任务信息
                if (!this.checkOneTask(cronTask)) {
                    return;
                }

                // 2.加载类和方法并运行
                try {
                    Class aClass = Class.forName(cronTask.getClassName());
                    Object bean = SpringContextUtils.getBean(aClass);
                    if (!StringUtils.equals(CommonConstrant.CRON_TASK_METHOD_RUNSCHEDULE,cronTask.getMethodName())){//不带参数
                        Method method = bean.getClass().getMethod(cronTask.getMethodName());
                        method.invoke(bean);
                    }else{//带参数
                        Long[] paramsNum = new Long[]{cronTask.getId()};

                        Class[] classes = new Class[paramsNum.length];
                        for(int i = 0; i < paramsNum.length;i++){
                            classes[i] = Long.class;
                        }
                        Method method = bean.getClass().getMethod(cronTask.getMethodName(),classes);
                        method.invoke(bean,cronTask.getId());
                    }
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error(cronTask.getClassName() + ": 读取任务接口异常", e.getMessage());
                }
            }
        };
    }


    /**
     * 新增任务时方法调用-刷新全部任务
     *    实现调度任务的灵活性
     */
    public void refreshAllTask(List<ScheduleTask> list) {
        // 已删除任务
        Set<Long> ids = scheduledFutures.keySet();
        for (Long id : ids) {
            if (notExist(id, list)) {
                scheduledFutures.get(id).cancel(false);
                scheduledFutures.remove(id);
                tasks.remove(id);
            }
        }
        list.forEach(e -> {refreshTask(e);});
        
    }


    // 判断任务是否存在
    private boolean isExist(Long id, List<ScheduleTask> list) {
        List<ScheduleTask> tasks = list.stream().filter(timedTask -> timedTask.getId() == id).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(tasks)){
            return true;
        }else {
            return false;
        }
    }



}

 4.调度线程池类

@Configuration
public class ScheduleThreadPool {

    /**
     * 核心线程5
     */
    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.initialize();
        taskScheduler.setPoolSize(5);
        taskScheduler.setRemoveOnCancelPolicy(true);
        taskScheduler.setThreadNamePrefix("thread-->");
        return taskScheduler;
    }

}

 5.启动初始化加载


@Configuration
@Slf4j
public class JustApplicationRunner implements ApplicationRunner {

    @Resource
    private ScheduledConfig scheduledConfig;
    @Resource
    private ScheduleService scheduleService;
    @Override
    public void run(ApplicationArguments args) {
        ScheduleTask task = new ScheduleTask();
        task.setActive(CommonConstrant.INTEGER_ONE);
        // 当即执行,该标识在代码中处理
        task.setCronExpression(CommonConstrant.CRON_NOW);                       scheduledConfig.refreshAllTask(scheduleService.getPage(task).getData().getRecords());
    }

}

6.数据库存储字段案例展示

 总结:以上只是简单的概述下本次使用的调度框架并记录下,也防止自己之后忘记这些。该调度框架可玩性还是挺高的,我之后把其直接抽出增加其他代码,比如异步任务和中间件等框架工具,整合成独立的项目降低耦合性,对其他项目进行调度开发,不失为一个好用的调度工具。

SpringBoot是一个非常流行的Java Web框架,而mybatis-plus是一个非常强大的ORM框架,两者合起来可以实现非常好用的定时任务管理。 下面是基于SpringBoot和mybatis-plus实现定时任务管理的步骤: 1. 添加依赖 在pom.xml文件中添加以下依赖: ```xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3.1</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.3.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> </dependencies> ``` 2. 配置数据库 在application.properties文件中添加以下配置: ``` # 数据库配置 spring.datasource.url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.jdbc.Driver # mybatis-plus配置 mybatis-plus.mapper-locations=classpath:/mapper/*.xml # Quartz配置 org.quartz.scheduler.instanceName = QuartzScheduler org.quartz.scheduler.instanceId = AUTO org.quartz.threadPool.threadCount=10 org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.dataSource=myDS org.quartz.jobStore.tablePrefix=QRTZ_ org.quartz.dataSource.myDS.driver=com.mysql.jdbc.Driver org.quartz.dataSource.myDS.URL=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&useSSL=false org.quartz.dataSource.myDS.user=root org.quartz.dataSource.myDS.password=123456 org.quartz.dataSource.myDS.maxConnections=5 ``` 3. 创建实体类和Mapper 创建一个定时任务的实体类,并在其中定义定时任务的属性: ```java @Data public class JobEntity { /** * 任务ID */ private String jobId; /** * 任务名称 */ private String jobName; /** * 任务分组 */ private String jobGroup; /** * 任务执行类 */ private String jobClass; /** * 任务描述 */ private String jobDescription; /** * 任务状态 0禁用 1启用 */ private Integer jobStatus; /** * 任务表达式 */ private String jobExpression; /** * 创建时间 */ private Date createTime; /** * 修改时间 */ private Date modifyTime; } ``` 创建一个定时任务的Mapper,并继承mybatis-plus的BaseMapper: ```java @Repository public interface JobMapper extends BaseMapper<JobEntity> { } ``` 4. 创建定时任务工具类 创建一个定时任务的工具类,用于实现定时任务的添加、删除、暂停和恢复等操作: ```java @Service public class QuartzService { @Autowired private Scheduler scheduler; /** * 添加任务 * * @param jobEntity */ public void addJob(JobEntity jobEntity) throws SchedulerException, ClassNotFoundException { // 获取任务类 Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(jobEntity.getJobClass()); // 创建任务 JobDetail jobDetail = JobBuilder.newJob(jobClass) .withIdentity(jobEntity.getJobName(), jobEntity.getJobGroup()) .withDescription(jobEntity.getJobDescription()) .storeDurably(true) .build(); // 创建触发器 Trigger trigger = TriggerBuilder.newTrigger() .withIdentity(jobEntity.getJobName(), jobEntity.getJobGroup()) .withSchedule(CronScheduleBuilder.cronSchedule(jobEntity.getJobExpression())) .build(); // 将任务和触发器添加到调度器 scheduler.scheduleJob(jobDetail, trigger); } /** * 删除任务 * * @param jobId * @param jobGroup */ public void deleteJob(String jobId, String jobGroup) throws SchedulerException { JobKey jobKey = new JobKey(jobId, jobGroup); scheduler.deleteJob(jobKey); } /** * 暂停任务 * * @param jobId * @param jobGroup */ public void pauseJob(String jobId, String jobGroup) throws SchedulerException { JobKey jobKey = new JobKey(jobId, jobGroup); scheduler.pauseJob(jobKey); } /** * 恢复任务 * * @param jobId * @param jobGroup */ public void resumeJob(String jobId, String jobGroup) throws SchedulerException { JobKey jobKey = new JobKey(jobId, jobGroup); scheduler.resumeJob(jobKey); } } ``` 5. 创建定时任务控制器 创建一个定时任务的控制器,用于实现定时任务的增删改查等操作: ```java @RestController @RequestMapping("/job") public class JobController { @Autowired private JobMapper jobMapper; @Autowired private QuartzService quartzService; /** * 添加任务 * * @param jobEntity */ @PostMapping("/add") public void addJob(@RequestBody JobEntity jobEntity) throws SchedulerException { // 添加任务 quartzService.addJob(jobEntity); // 保存任务到数据库 jobMapper.insert(jobEntity); } /** * 删除任务 * * @param jobId * @param jobGroup */ @DeleteMapping("/delete") public void deleteJob(String jobId, String jobGroup) throws SchedulerException { // 删除任务 quartzService.deleteJob(jobId, jobGroup); // 从数据库中删除任务 QueryWrapper<JobEntity> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("job_id", jobId); queryWrapper.eq("job_group", jobGroup); jobMapper.delete(queryWrapper); } /** * 暂停任务 * * @param jobId * @param jobGroup */ @PutMapping("/pause") public void pauseJob(String jobId, String jobGroup) throws SchedulerException { // 暂停任务 quartzService.pauseJob(jobId, jobGroup); // 更新任务状态 JobEntity jobEntity = new JobEntity(); jobEntity.setJobId(jobId); jobEntity.setJobGroup(jobGroup); jobEntity.setJobStatus(0); jobMapper.updateById(jobEntity); } /** * 恢复任务 * * @param jobId * @param jobGroup */ @PutMapping("/resume") public void resumeJob(String jobId, String jobGroup) throws SchedulerException { // 恢复任务 quartzService.resumeJob(jobId, jobGroup); // 更新任务状态 JobEntity jobEntity = new JobEntity(); jobEntity.setJobId(jobId); jobEntity.setJobGroup(jobGroup); jobEntity.setJobStatus(1); jobMapper.updateById(jobEntity); } /** * 获取任务列表 * * @return */ @GetMapping("/list") public List<JobEntity> listJob() { return jobMapper.selectList(null); } } ``` 至此,基于SpringBoot和mybatis-plus实现定时任务管理的步骤就完成了。我们可以通过访问控制器提供的接口来实现定时任务的添加、删除、暂停和恢复等操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值