springboot 与数据库动态加载cron表达式任务

config

@EnableScheduling
@Configuration
public class SchedulingConfig {


    @Autowired
    private TaskSolverChooser taskSolverChooser;

    @Bean
    public MyScheduledTask createMyScheduledTask(){
        return new MyScheduledTask(taskSolverChooser);
    }

}

接口

package com.feifan.aida.outfit.scheduling.service;

public interface TaskService {

    void handlerJob(String param);

    String type();
}

实体类

package com.feifan.aida.outfit.scheduling;

import lombok.Data;

@Data
public class TaskEntityDTO {

    private String taskId;

    /**
     * 任务id
     */
    private String type;


    private String param;

    /***
     * 其他 无效
     * 1 有效
     */
    private Integer valid;

    /**
     * cron 表达式
     */
    private String cron;
}

任务加载

package com.feifan.aida.outfit.scheduling;

import com.feifan.aida.outfit.scheduling.service.TaskService;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

@Component
public class TaskSolverChooser implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    private Map<String, TaskService> chooseMap = new HashMap<>(16);

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @PostConstruct
    private void registerToTaskSolver(){
        Map<String, TaskService> taskServiceMap = applicationContext.getBeansOfType(TaskService.class);
        for (TaskService value : taskServiceMap.values()) {
            chooseMap.put(value.type(), value);
        }
    }

    /**
     * 获取需要的job
     */
    public TaskService getTask(String type){
        return chooseMap.get(type);
    }


}

任务处理逻辑

package com.feifan.aida.outfit.scheduling;

import com.feifan.aida.outfit.scheduling.service.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.PreDestroy;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;
@Slf4j
public class MyScheduledTask implements SchedulingConfigurer {

private volatile ScheduledTaskRegistrar registrar;

private final ConcurrentHashMap<String, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, CronTask> cronTasks = new ConcurrentHashMap<>();

private TaskSolverChooser taskSolverChooser;

public MyScheduledTask(TaskSolverChooser taskSolverChooser){
this.taskSolverChooser = taskSolverChooser;
}

@Override
public void configureTasks(ScheduledTaskRegistrar registrar) {

//设置20个线程,默认单线程,如果不设置的话,不能同时并发执行任务
registrar.setScheduler(Executors.newScheduledThreadPool(10));
this.registrar = registrar;
}

/**
* 修改 cron 需要 调用该方法
*/
public void refresh(List<TaskEntityDTO> tasks){
//取消已经删除的策略任务
Set<String> sids = scheduledFutures.keySet();
for (String sid : sids) {
if(!exists(tasks, sid)){
scheduledFutures.get(sid).cancel(false);
}
}
for (TaskEntityDTO taskEntityDTO : tasks) {
String cron = taskEntityDTO.getCron();
//计划任务表达式为空则跳过
if(!StringUtils.hasLength(cron)){
remove(taskEntityDTO);
continue;
}
//计划任务已存在并且表达式未发生变化则跳过
if (scheduledFutures.containsKey(taskEntityDTO.getTaskId())
&& cronTasks.get(taskEntityDTO.getTaskId()).getExpression().equals(cron)) {
remove(taskEntityDTO);
continue;
}
//如果策略执行时间发生了变化,则取消当前策略的任务
if(scheduledFutures.containsKey(taskEntityDTO.getTaskId())){
remove(taskEntityDTO);
}
//ismatch 判断
if(ObjectUtils.isEmpty(taskEntityDTO.getValid())&&!"1".equals(String.valueOf(taskEntityDTO.getValid()))){
remove(taskEntityDTO);
}
//业务逻辑处理
CronTask task = cronTask(taskEntityDTO);

//执行业务
ScheduledFuture<?> future = registrar.getScheduler().schedule(task.getRunnable(), task.getTrigger());
cronTasks.put(taskEntityDTO.getTaskId(), task);
scheduledFutures.put(taskEntityDTO.getTaskId(), future);
}
}

private void remove(TaskEntityDTO taskEntityDTO){
ScheduledFuture<?> scheduledFuture = scheduledFutures.remove(taskEntityDTO.getTaskId( ));
if(!ObjectUtils.isEmpty(scheduledFuture)){
scheduledFutures.get(taskEntityDTO.getTaskId()).cancel(false);
scheduledFutures.remove(taskEntityDTO.getTaskId());
cronTasks.remove(taskEntityDTO.getTaskId());
}
}


/**
* 停止 cron 运行
*/
public void stop(List<TaskEntityDTO> tasks){
tasks.forEach(item->{
if (scheduledFutures.containsKey(item.getTaskId())) {
// mayInterruptIfRunning设成false话,不允许在线程运行时中断,设成true的话就允许。
scheduledFutures.get(item.getTaskId()).cancel(false);
scheduledFutures.remove(item.getTaskId());
}
});
}

/**
* 业务逻辑处理
*/
public CronTask cronTask(TaskEntityDTO taskEntityDTO) {
return new CronTask(() -> {
//每个计划任务实际需要执行的具体业务逻辑
//采用策略,模式 ,执行我们的job
TaskService task = taskSolverChooser.getTask(taskEntityDTO.getType());
task.handlerJob(taskEntityDTO.getParam());
}, taskEntityDTO.getCron());
}

private boolean exists(List<TaskEntityDTO> tasks, String tid){
for(TaskEntityDTO taskEntityDTO:tasks){
if(taskEntityDTO.getTaskId().equals(tid)){
return true;
}
}
return false;
}

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

}

参考文档:https://www.jb51.net/article/207667.htm

posted @ 2022-10-30 14:30  随风而逝,只是飘零  阅读(666)  评论(0编辑  收藏  举报