定时任务调度Demo

简介:通过创建线程池的方式完成定时任务调度。

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 根据业务需要修改成自己的就可以

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值