1 使用ScheduledFuture 自定义定时任务。
2 通过持久化定时任务,任务包括任务名称,描述,规则,状态,制定任务执行的具体线程,参数等,通过rest风格接口调用来crud任务 ,启动任务,停止任务。
3 任务线程类中处理具体的业务。
- 自定义任务实体类
@Entity
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class ScheduleTask {
/**
* Id
*/
@Id
@GeneratedValue(generator = "idGenerator")
@GenericGenerator(name ="idGenerator" ,strategy="org.common.ids.SerialIdGeneratorSnowflakeId")
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
/**
* 任务名称
*/
@Column(name = "NAME")
@ApiModelProperty("任务名称")
private String name;
@Column(name = "DESC_TXT",columnDefinition = "text")
@ApiModelProperty("任务描述")
private String descTxt;
/**
* 任务描述
*/
@Column(name = "CRON")
@ApiModelProperty("任务cron @Schedule 规则")
private String cron;
/**
* 任务状态
*/
@Column(name = "STATUS")
@Enumerated(EnumType.STRING)
@ApiModelProperty("任务状态")
private Status status;
/**
* 开关
*/
@Column(name = "deleted")
@ApiModelProperty("是否删除")
private Boolean deleted = false;
/**
* 执行指定类
* 指定具体的包名
*/
@Column(name = "EXE_CLASS")
@ApiModelProperty("执行指定类 com.test.autojob.xxxRunnable")
private String exeClass;
/**
* 任务参数 json
*/
@Column(name = "PARAMS")
@ApiModelProperty("任务参数")
private String params;
@Column(name = "CREATE_TIME")
private LocalDateTime createTime;
@Column(name = "UPDATE_TIME")
private LocalDateTime updateTime;
public enum Status{
create,
running,
stop
}
}
- 自定义任务控制层
/**
* 自定义定时任务
* @author zhangwx
* @date 2022/07/22 14:05
*/
@RestController
@Api(tags = "自定义定时任务")
public class ScheduleTaskController {
private ScheduleTaskService scheduleTaskService;
public ScheduleTaskController(ScheduleTaskService scheduleTaskService) {
this.scheduleTaskService = scheduleTaskService;
}
@PostConstruct
public void init(){
scheduleTaskService.startAll();
}
/**
* 保存
* @param scheduleTask
* @return
*/
@PostMapping("/save")
@ApiOperation("新增或保存自定义任务")
public ReturnData<ScheduleTask> save(@RequestBody ScheduleTask scheduleTask){
try {
ScheduleTask task= scheduleTaskService.save(scheduleTask);
return new ReturnData<>(ReturnData.SUCCESS_CODE,"保存成功",task,null);
} catch (Exception e) {
e.printStackTrace();
return new ReturnData<>(ReturnData.FAIL_CODE,"保存失败"+e.getMessage(),null,null);
}
}
/**
* 启动
* @param taskId
* @return
*/
@GetMapping("/start/{taskId}")
@ApiOperation("启动任务")
public ReturnData<String> start(@PathVariable(value = "taskId") Long taskId){
try {
scheduleTaskService.start(taskId);
return new ReturnData<>(ReturnData.SUCCESS_CODE,"启动成功",null,null);
} catch (Exception e) {
e.printStackTrace();
return new ReturnData<>(ReturnData.FAIL_CODE,"启动失败"+e.getMessage(),null,null);
}
}
/**
* 重启
* @param taskId
* @return
*/
@GetMapping("/restart/{taskId}")
@ApiOperation("重启")
public ReturnData<String> restart(@PathVariable(value = "taskId") Long taskId){
try {
scheduleTaskService.restart(taskId);
return new ReturnData<>(ReturnData.SUCCESS_CODE,"重启成功",null,null);
} catch (Exception e) {
e.printStackTrace();
return new ReturnData<>(ReturnData.FAIL_CODE,"重启失败"+e.getMessage(),null,null);
}
}
/**
* 停止任务
* @param taskId
* @return
*/
@GetMapping("/stop/{taskId}")
@ApiOperation("停止任务")
public ReturnData<String> stop(@PathVariable(value = "taskId") Long taskId){
try {
scheduleTaskService.stop(taskId);
return new ReturnData<>(ReturnData.SUCCESS_CODE,"停止成功",null,null);
} catch (Exception e) {
e.printStackTrace();
return new ReturnData<>(ReturnData.FAIL_CODE,"停止失败"+e.getMessage(),null,null);
}
}
}
- 自定义任务业务层
/**
* @author zhangwx
* @date 2022/07/22 13:52
*/
@Service
public class ScheduleTaskServiceImpl implements ScheduleTaskService {
private ScheduleTaskDao scheduleTaskDao;
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
private ApplicationContext applicationContext;
/**
* 正在运行的任务
*/
public static ConcurrentHashMap<Long,ScheduledFuture> runTasKMap = new ConcurrentHashMap<>();
public ScheduleTaskServiceImpl(ScheduleTaskDao scheduleTaskDao,
ThreadPoolTaskScheduler threadPoolTaskScheduler,
ApplicationContext applicationContext) {
this.scheduleTaskDao = scheduleTaskDao;
this.threadPoolTaskScheduler = threadPoolTaskScheduler;
this.applicationContext = applicationContext;
}
@Override
public List<ScheduleTask> findAllTasks() {
return scheduleTaskDao.findAllByDeleted(false);
}
@Override
public ReturnData<String> startAll() {
List<ScheduleTask> allTasks = findAllTasks();
for (ScheduleTask task : allTasks) {
if (runTasKMap.containsKey(task.getId())){
continue;
}
try {
this.start(task.getId());
} catch (Exception e) {
e.printStackTrace();
}
}
return new ReturnData<>(ReturnData.SUCCESS_CODE,"所有任务启动成功",null,null);
}
@Override
public ScheduleTask save(ScheduleTask scheduleTask) {
return scheduleTaskDao.saveAndFlush(scheduleTask);
}
@Override
public ReturnData<String> start(Long taskId) throws Exception{
ScheduleTask scheduleTask = scheduleTaskDao.findById(taskId).orElse(null);
this.assertTaskNotRunning(scheduleTask);
Runnable runnable = getTaskRunnable(scheduleTask);
CronTrigger trigger = new CronTrigger(scheduleTask.getCron());
ScheduledFuture<?> future = threadPoolTaskScheduler.schedule(runnable, trigger);
assert future != null;
runTasKMap.put(taskId,future);
this.updateTaskStatus(scheduleTask,ScheduleTask.Status.running);
return new ReturnData<>(ReturnData.SUCCESS_CODE,"启动成功",null,null);
}
private JobRunnables getTaskRunnable(ScheduleTask scheduleTask) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class<?> aClass = Class.forName(scheduleTask.getExeClass());
//创建job对象
Constructor<?> constructor = aClass.getConstructor(String.class,ApplicationContext.class);
return (JobRunnables) constructor.newInstance(scheduleTask.getParams(),applicationContext);
}
@Override
public ReturnData<String> restart(Long taskId) throws Exception{
stop(taskId);
start(taskId);
return new ReturnData<>(ReturnData.SUCCESS_CODE,"任务重启成功",null,null);
}
@Override
public ReturnData<String> stop(Long taskId) throws Exception{
ScheduledFuture scheduledFuture = runTasKMap.get(taskId);
if (null == scheduledFuture){
return new ReturnData<>(ReturnData.FAIL_CODE,"没有执行中的任务",null,null);
}
boolean cancel = scheduledFuture.cancel(true);
if (cancel){
runTasKMap.remove(taskId);
}
updateTaskStatus(scheduleTaskDao.findById(taskId).orElse(null), ScheduleTask.Status.stop);
return new ReturnData<>(ReturnData.SUCCESS_CODE,"任务停止成功",null,null);
}
private void updateTaskStatus(ScheduleTask scheduleTask, ScheduleTask.Status status) {
if (null == scheduleTask){
return;
}
scheduleTask.setStatus(status);
scheduleTask.setUpdateTime(LocalDateTime.now());
scheduleTaskDao.save(scheduleTask);
}
private void assertTaskNotRunning(ScheduleTask scheduleTask) throws ScheduleTaskException {
if (null == scheduleTask){
throw new ScheduleTaskException("查询不到任务");
}
ScheduledFuture scheduledFuture = runTasKMap.get(scheduleTask.getId());
if (null != scheduledFuture){
throw new ScheduleTaskException("任务正在运行中");
}
// if (ScheduleTask.Status.running.equals(scheduleTask.getStatus())){
// throw new ScheduleTaskException("任务正在运行中");
// }
}
}
- 自定义任务dao层
public interface ScheduleTaskDao extends JpaRepository<ScheduleTask,Long>, JpaSpecificationExecutor<ScheduleTask> {
List<ScheduleTask> findAllByDeleted(Boolean deleted);
}
- 自定义任务
/**
* @author zhangwx 抽象的业务任务执行线程
* @date 2022/07/22 16:38
*/
public abstract class JobRunnables implements Runnable {
private String params;
private ApplicationContext applicationContext;
public JobRunnables(String params, ApplicationContext applicationContext) {
this.params = params;
this.applicationContext = applicationContext;
}
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params;
}
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
/**
* 具体的业务job
* @author zhangwx
* @date 2022/07/22 16:42
*/
@Slf4j
public class AutoWithdrawalRunnable extends JobRunnables {
private final TestFeign testFeign;
private final MerchantCacheService merchantCacheService;
public AutoWithdrawalRunnable(String params, ApplicationContext applicationContext) {
super(params, applicationContext);
FeignContext feignContext = applicationContext.getBean("feignContext", FeignContext.class);
merchantCacheService = applicationContext.getBean("merchantCacheServiceImpl",MerchantCacheService.class);
testFeign = feignContext.getInstance("testFeign", TestFeign.class);
}
@Override
public void run() {
try {
String param= getParams();
//todo 具体业务实现类
} catch (Exception e) {
log.error("自动提现异常,msg:"+e.getMessage(),e);
}
}
}