简介:通过创建线程池的方式完成定时任务调度。
entity:用来存储任务信息的实体类
根据业务场景的不同 还需要在数据库中去创建一张自己任务表,该实体不会直接用作维护任务的实体类。
package com.izesion.entity;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class ScheduledTask {
/**
* 任务key值 唯一
*/
private String taskKey;
/**
* 任务描述
*/
private String taskDesc;
/**
* 任务表达式
*/
private String taskCron;
/**
* 程序初始化是否启动 1 是 0 否
*/
private Integer initStartFlag;
/**
* 当前是否已启动
*/
private boolean startFlag;
private String className;
}
config:创建线程池的一些配置
package com.izesion.task.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class ScheduledTaskConfig {
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
log.info("创建定时任务调度线程池 start");
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(20);
threadPoolTaskScheduler.setThreadNamePrefix("taskExecutor-");
threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
threadPoolTaskScheduler.setAwaitTerminationSeconds(60);
log.info("创建定时任务调度线程池 end");
return threadPoolTaskScheduler;
}
}
util:后续会使用到的Spring的工具类,可以通过SpringUtils来获取Service
package io.renren.commons.tools.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
//@Lazy(false)
public class SpringUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
controller:暴露出来的接口,该接口管理内存中存储的任务信息,暴露给前端的接口需要另外写一套基础CRUD将任务信息维护到数据库,并通过调这个接口去管理内存中的任务。
package com.izesion.task.controller;
import com.izesion.entity.ScheduledTask;
import com.izesion.service.ScheduledTaskService;
import io.renren.commons.tools.utils.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/task")
@Api(tags = "定时任务相关接口")
@Slf4j
public class TaskController {
@Autowired
private ScheduledTaskService scheduledTaskService;
@GetMapping("/list")
@ApiOperation("获取所有list")
public Result<List<ScheduledTask>> list() {
List<ScheduledTask> scheduledTasks = scheduledTaskService.taskList();
return new Result<List<ScheduledTask>>().ok(scheduledTasks);
}
@GetMapping("/start/{taskKey}")
@ApiOperation("开始定时任务")
public Result<Boolean> start(@PathVariable(name = "taskKey") String taskKey) {
if (!scheduledTaskService.isExist(taskKey)) {
log.error("{}任务不存在", taskKey);
return new Result<Boolean>().error();
}
return new Result<Boolean>().ok(scheduledTaskService.start(taskKey));
}
@GetMapping("/stop/{taskKey}")
@ApiOperation("停止定时任务")
public Result<Boolean> stop(@PathVariable(name = "taskKey") String taskKey) {
if (!scheduledTaskService.isExist(taskKey)) {
log.error("{}任务不存在", taskKey);
return new Result<Boolean>().error();
}
return new Result<Boolean>().ok(scheduledTaskService.stop(taskKey));
}
@PostMapping("/update")
@ApiOperation("更新任务")
public Result<Boolean> update(@RequestBody ScheduledTask scheduledTask) {
if (scheduledTaskService.isStart(scheduledTask.getTaskKey())) {
log.error("{}任务正在运行", scheduledTask.getTaskKey());
return new Result<Boolean>().error();
}
ScheduledTask local = scheduledTaskService.getTaskEntity(scheduledTask.getTaskKey());
if (local != null) {
local.setTaskCron(scheduledTask.getTaskCron());
local.setTaskDesc(scheduledTask.getTaskDesc());
local.setInitStartFlag(scheduledTask.getInitStartFlag());
scheduledTaskService.putTaskEntity(scheduledTask.getTaskKey(), local);
}
return new Result<Boolean>().ok(null);
}
}
servic:service层接口
package com.izesion.service;
import com.izesion.entity.ScheduledTask;
import java.util.List;
public interface ScheduledTaskService {
/**
* 所有任务列表
* @return list
*/
List<ScheduledTask> taskList();
/**
* 根据任务key 启动任务
* @param taskKey taskKey
* @return boolean
*/
Boolean start(String taskKey);
/**
* 根据任务key 停止任务
* @param taskKey taskKey
* @return boolean
*/
Boolean stop(String taskKey);
/**
* 根据任务key 重启任务
* @param taskKey taskKey
* @return boolean
*/
Boolean restart(String taskKey);
/**
* 移除任务
* @param taskKey taskKey
* @return boolean
*/
Boolean remove(String taskKey);
/**
* 是否存在该
* @param taskKey taskKey
* @return boolean
*/
Boolean isExist(String taskKey);
/**
* 判断是否运行
* @param taskKey taskKey
* @return boolean
*/
Boolean isStart(String taskKey);
/**
* 插入任务
*
* @param taskKey taskKey
* @param job job
*/
void putTask(String taskKey, ScheduledTaskJob job);
/**
* 获取任务
*
* @param taskKey taskKey
* @return job
*/
ScheduledTaskJob getTask(String taskKey);
/**
* 插入任务实例
*
* @param taskKey taskKey
* @param task task
*/
void putTaskEntity(String taskKey, ScheduledTask task);
/**
* 程序启动时初始化 ==> 启动所有正常状态的任务
* @param list list
*/
void initAllTask(List<ScheduledTask> list);
/**
* 根据taskKey获取任务实例
*
* @param taskKey taskKey
* @return 任务实例
*/
ScheduledTask getTaskEntity(String taskKey);
}
impl:具体的实现
package com.izesion.task.impl;
import com.izesion.entity.ScheduledTask;
import com.izesion.service.ScheduledTaskJob;
import com.izesion.service.ScheduledTaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j
@Service
public class ScheduledTaskServiceImpl implements ScheduledTaskService {
/**
* 可重入锁
*/
private ReentrantLock lock = new ReentrantLock();
/**
* 定时任务线程池
*/
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
/**
* 所有定时任务存放Map
* key :任务 key
* value:任务实现
*/
private Map<String, ScheduledTaskJob> scheduledTaskJobMap = new ConcurrentHashMap<>();
private Map<String, ScheduledTask> scheduledTasks = new ConcurrentHashMap<>();
/**
* 存放已经启动的任务map
*/
private Map<String, ScheduledFuture> scheduledFutureMap = new ConcurrentHashMap<>();
@Override
public void putTask(String taskKey, ScheduledTaskJob job) {
scheduledTaskJobMap.put(taskKey, job);
}
@Override
public ScheduledTaskJob getTask(String taskKey) {
return scheduledTaskJobMap.get(taskKey);
}
@Override
public void putTaskEntity(String taskKey, ScheduledTask task) {
scheduledTasks.put(taskKey, task);
}
/**
* 所有任务列表
*/
@Override
public List<ScheduledTask> taskList() {
log.info(">>>>>> 获取任务列表开始 >>>>>> ");
//数据库查询所有任务 => 未做分页
List<ScheduledTask> taskBeanList = new ArrayList<>(scheduledTasks.values());
if (CollectionUtils.isEmpty(taskBeanList)) {
return Collections.emptyList();
}
for (ScheduledTask taskBean : taskBeanList) {
String taskKey = taskBean.getTaskKey();
//是否启动标记处理
taskBean.setStartFlag(this.isStart(taskKey));
}
log.info(">>>>>> 获取任务列表结束 >>>>>> ");
return taskBeanList;
}
/**
* 根据任务key 启动任务
*/
@Override
public Boolean start(String taskKey) {
log.info(">>>>>> 启动任务 {} 开始 >>>>>>", taskKey);
//添加锁放一个线程启动,防止多人启动多次
log.info(">>>>>> 添加任务启动锁完毕");
try {
lock.lock();
//校验是否已经启动
if (this.isStart(taskKey)) {
log.info(">>>>>> 当前任务已经启动,无需重复启动!");
return false;
}
//校验任务是否存在
if (!scheduledTaskJobMap.containsKey(taskKey)) {
return false;
}
//根据key数据库获取任务配置信息
ScheduledTask scheduledTask = scheduledTasks.get(taskKey);
//启动任务
this.doStartTask(scheduledTask);
} finally {
lock.unlock();
// 释放锁
log.info(">>>>>> 释放任务启动锁完毕");
}
log.info(">>>>>> 启动任务 {} 结束 >>>>>>", taskKey);
return true;
}
/**
* 根据 key 停止任务
*/
@Override
public Boolean stop(String taskKey) {
log.info(">>>>>> 进入停止任务 {} >>>>>>", taskKey);
//当前任务实例是否存在
boolean taskStartFlag = scheduledFutureMap.containsKey(taskKey);
log.info(">>>>>> 当前任务实例是否存在 {}", taskStartFlag);
if (taskStartFlag) {
//获取任务实例
ScheduledFuture scheduledFuture = scheduledFutureMap.get(taskKey);
//关闭实例
scheduledFuture.cancel(true);
}
log.info(">>>>>> 结束停止任务 {} >>>>>>", taskKey);
return taskStartFlag;
}
/**
* 根据任务key 重启任务
*/
@Override
public Boolean restart(String taskKey) {
log.info(">>>>>> 进入重启任务 {} >>>>>>", taskKey);
//先停止
this.stop(taskKey);
//再启动
return this.start(taskKey);
}
@Override
public Boolean remove(String taskKey) {
if (!this.stop(taskKey)) {
return false;
}
this.scheduledTaskJobMap.remove(taskKey);
this.scheduledTasks.remove(taskKey);
this.scheduledFutureMap.remove(taskKey);
return true;
}
@Override
public Boolean isExist(String taskKey) {
return scheduledTaskJobMap.containsKey(taskKey);
}
/**
* 程序启动时初始化 ==> 启动所有正常状态的任务
*/
@Override
public void initAllTask(List<ScheduledTask> scheduledTaskBeanList) {
log.info("程序启动 ==> 初始化所有任务开始 !size={}", scheduledTaskBeanList.size());
if (CollectionUtils.isEmpty(scheduledTaskBeanList)) {
return;
}
for (ScheduledTask scheduledTask : scheduledTaskBeanList) {
//任务 key
String taskKey = scheduledTask.getTaskKey();
//校验是否已经启动
if (this.isStart(taskKey)) {
continue;
}
//启动任务
this.doStartTask(scheduledTask);
}
log.info("程序启动 ==> 初始化所有任务结束 !size={}", scheduledTaskBeanList.size());
}
/**
* 执行启动任务
*/
private void doStartTask(ScheduledTask scheduledTask) {
//任务key
String taskKey = scheduledTask.getTaskKey();
//定时表达式
String taskCron = scheduledTask.getTaskCron();
//获取需要定时调度的接口
ScheduledTaskJob scheduledTaskJob = scheduledTaskJobMap.get(taskKey);
log.info("==============================================================任务========================>>>{}",scheduledTaskJob.getClass());
log.info(">>>>>> 任务 [ {} ] ,cron={}", scheduledTask.getTaskDesc(), taskCron);
ScheduledFuture scheduledFuture = threadPoolTaskScheduler.schedule(scheduledTaskJob,
triggerContext -> {
CronTrigger cronTrigger = new CronTrigger(taskCron);
return cronTrigger.nextExecutionTime(triggerContext);
});
//将启动的任务放入 map
scheduledFutureMap.put(taskKey, scheduledFuture);
}
/**
* 任务是否已经启动
*/
@Override
public Boolean isStart(String taskKey) {
//校验是否已经启动
if (scheduledFutureMap.containsKey(taskKey)) {
return !scheduledFutureMap.get(taskKey).isCancelled();
}
return false;
}
@Override
public ScheduledTask getTaskEntity(String taskKey) {
return scheduledTasks.get(taskKey);
}
}
task:任务接口
package com.izesion.service;
public interface ScheduledTaskJob extends Runnable {
}
这里是项目启动时的初始化线程,在这里面去调用具体需要执行的逻辑线程(根据项目的具体需要去修改)。
package com.izesion.task.job;
import com.izesion.service.MetadataPartitionCollectService;
import com.izesion.service.ScheduledTaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 初始化定式任务
*/
@Slf4j
@Component
@Order(value = 4)
public class InitMetaDataCollectionTask implements CommandLineRunner {
@Autowired
private ScheduledTaskService scheduledTaskService;
//数据库中存储定时任务配置的表
@Resource
private TargetService targetService;
@Override
public void run(String... args) {
// log.info(">>>>>> 初始化开始 <<<<<< ");
// //数据库中查询所有的开启的任务
// List<TargetVO> list = targetService.list(new HashMap<>());
// list.forEach(e->{
// //创建定时任务
// scheduledTaskService.putTask(e.getTaskKey(), new TargetTask(e.getId(),e.getTaskKey(),"sql"));
// ScheduledTask scheduledTask = new ScheduledTask().setTaskCron(e.getCron()).setTaskKey(e.getTaskKey());
// scheduledTaskService.putTaskEntity(e.getTaskKey(), scheduledTask);
// scheduledTaskService.start(e.getTaskKey());
// });
// log.info(">>>>>> 初始化结束 <<<<<< ");
}
}
根据项目的需求自己定义的需要执行的逻辑线程,这里只是说明如何传入一些可能需要的参数。
package com.izesion.task.job;
import com.izesion.service.MetadataPartitionCollectService;
import com.izesion.service.ScheduledTaskJob;
import io.renren.commons.tools.utils.SpringUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TargetTask implements ScheduledTaskJob {
//任务id
private Long taskId;
//任务key
private String taskKey;
//sql
private String sql;
//要使用其他地方的service 只能从Spring容器中拿 不能直接使用 @Resource或者@Autowired
private TargetService targetService;
public TargetTask (Long taskId, String taskKey, String sql) {
this.taskId= taskId;
this.taskKey = taskKey;
this.sql = sql;
this.targetService= SpringUtils.getBean(TargetService.class);
}
@SneakyThrows
@Override
public void run() {
//定式任务执行的逻辑代码
targetService.executeTask(taskId);
}
}
代码中的 targetService 和targetTask 根据业务需要修改成自己的就可以