SpringBoot dynamic timing task

SpringBoot dynamic timing task

Recently, I encountered a scenario in which springBoot dynamically executes timing tasks in my work. I dynamically specify the execution frequency of tasks through the database, disable or start timing tasks, etc., by searching for information, I successfully solved this scenario, let's share it together

1 Database design:

CREATE TABLE `spring_scheduled_cron` (
  `cron_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `cron_key` varchar(128) NOT NULL COMMENT '定时任务完整类名(该类必须继承 TimingTask类)',
  `cron_expression` varchar(20) NOT NULL COMMENT 'cron表达式',
  `task_explain` varchar(50) NOT NULL DEFAULT '' COMMENT '任务描述',
  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态,1:正常;2:停用',
  PRIMARY KEY (`cron_id`),
  UNIQUE KEY `cron_key` (`cron_key`),
  UNIQUE KEY `cron_key_unique_idx` (`cron_key`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务表';

2 Write a Mapper to query the database

public interface ScheduleMapper {
    
    
    /**
     * 获取有效得定时任务
     * */
    @Select(" select cron_id,cron_key,cron_expression,task_explain,status 
              from spring_scheduled_cron ")
    public List<Schedule> getAll();
}

3 Custom task class

public abstract class TimingTask implements Runnable {
    
    
   private Schedule schedule;


    public Schedule getSchedule() {
    
    
        return schedule;
    }


    public void setSchedule(Schedule schedule) {
    
    
        this.schedule = schedule;
    }

    @Override
    public  void run() {
    
    
        this.task();
    }

    //执行的任务
    public abstract void  task();


    @Override
    public String toString() {
    
    
        return schedule.getCronId()+"   "+schedule.getCronKey();
    }

}

4 Write a dynamic task configuration class

@Configuration
@EnableScheduling
public class DynamicTask implements SchedulingConfigurer {
    
    
    private static Logger LOGGER = LoggerFactory.getLogger(DynamicTask.class);

    //通过 registrar 可以注册 定时任务
    private volatile ScheduledTaskRegistrar registrar;


    //存放执行器
    private final ConcurrentHashMap<Integer, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<>();

    //存放当前执行的定时任务
    private final ConcurrentHashMap<Integer, CronTask> cronTasks = new ConcurrentHashMap<>();


    @Autowired
    private ScheduleMapper scheduleMapper;

    @Autowired
    private ApplicationContext applicationContext;



    @Override
    public void configureTasks(ScheduledTaskRegistrar registrar) {
    
    
        this.registrar = registrar;


        this.registrar.addTriggerTask(() -> {
    
    
            //查询定时任务
            List<Schedule> scheduleList = scheduleMapper.getAll();
            createTask(scheduleList);
        }
        , triggerContext -> new PeriodicTrigger(5L, TimeUnit.SECONDS).nextExecutionTime(triggerContext));

    }


    public void createTask(List<Schedule> scheduleList){
    
    

        if (!CollectionUtils.isEmpty(scheduleList)) {
    
    
            LOGGER.info("检测动态定时任务列表...");
            List<TimingTask> tts = new ArrayList<>();

            scheduleList .forEach(taskConstant -> {
    
    
                Object bean = null;
                try {
    
    
                    Class<?> taskClass = Class.forName(taskConstant.getCronKey());
                    //创建定时任务 去执行
                    bean = applicationContext.getBean(taskClass);
                } catch (ClassNotFoundException e) {
    
    
                    LOGGER.info("定时任务执行器{} 在Spring内找不到!",taskConstant.getCronKey());
                    return;
                }

                //获取bean后转换未 TimingTask 对象
                if(bean instanceof  TimingTask){
    
    
                    TimingTask timingTask =  (TimingTask)bean;
                   timingTask.setSchedule(taskConstant);
                    tts.add(timingTask);
                }
            });
            this.refreshTasks(tts.toArray(new TimingTask[tts.size()]));
        }
        LOGGER.info("定时任务加载完成!");

    }




    private void refreshTasks(TimingTask... tasks) {
    
    

        Set<Integer> taskIds = scheduledFutures.keySet();

        //查看任务是否已经存在,如果存在了就取消
        for (Integer taskId : taskIds) {
    
    
            if (!exists(taskId,tasks)) {
    
    
                scheduledFutures.get(taskId).cancel(false);
            }
        }

        for (TimingTask timingTask : tasks) {
    
    

            //校验 cron 表达式是否合法
            String expression = timingTask.getSchedule().getCronExpression();
            if (StringUtils.isBlank(expression) || !CronSequenceGenerator.isValidExpression(expression)) {
    
    
                LOGGER.error("定时任务"+timingTask.getSchedule().getCronKey()+" cron表达式不合法: " + expression);
                continue;
            }


            //如果配置一致,并且处于启用状态 则不需要重新创建定时任务
            if (scheduledFutures.containsKey(timingTask.getSchedule().getCronId())
                    && cronTasks.get(timingTask.getSchedule().getCronId()).getExpression().equals(expression) && timingTask.getSchedule().getStatus().equals("1")) {
    
    
                continue;
            }


            //如果策略执行时间发生了变化(如cron表达式修改,状态修改),则取消当前策略的任务,重新创建
            if (scheduledFutures.containsKey(timingTask.getSchedule().getCronId())) {
    
    
                scheduledFutures.remove(timingTask.getSchedule().getCronId()).cancel(false);
                cronTasks.remove(timingTask.getSchedule().getCronId());
            }

            //如果任务有效,才创建
           if(timingTask.getSchedule().getStatus().equals("1")){
    
    
               //创建定时任务执行
               CronTask task = new CronTask(timingTask, expression);
               ScheduledFuture<?> future = registrar.getScheduler().schedule(task.getRunnable(), task.getTrigger());
               cronTasks.put(timingTask.getSchedule().getCronId(), task);

               scheduledFutures.put(timingTask.getSchedule().getCronId(), future);
               LOGGER.info("添加定时任务===>{}   Cron表达式==>{}",timingTask.getSchedule().getCronKey(),timingTask.getSchedule().getCronExpression());
           }
        }
    }

    private boolean exists(Integer taskId,TimingTask... tasks) {
    
    
        for (TimingTask task : tasks) {
    
    
            if (task.getSchedule().getCronId().equals(taskId)) {
    
    
                return true;
            }
        }
        return false;
    }



    @PreDestroy
    public void destroy() {
    
    
        this.registrar.destroy();
    }
}

You're done, start the test:

Insert data into the database, the timing task can run normally, and the start and stop of the timing task can be controlled by changing the cron expression or state of the database without restarting the server, and the execution frequency of the timing task can be modified.


GitHub地址:Spring-Boot-Schedule

Referred to several blogs of big guys: I hope it will be useful to you as well:

Springboot timing task principle and how to dynamically create timing tasks

Dynamically implement timing task configuration in Spring Boot

Guess you like

Origin blog.csdn.net/dndndnnffj/article/details/109674404