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();
}
}