springboot定时任务

springboot定时任务

需求场景:一个消息通知系统,新建通知设定指定时间后自动发布。
需求分析:新建消息后数据存入数据库中,然后通过定时任务去触发发布事件。
实现①:新增后直接将该消息加入到定时任务的延迟队列当中,到时候后自动发布。
实现②:新增2个定时任务,定时任务1每隔 a 时间去数据库取出 b 时间内将要发布的通知(其中a<<b), 然后将其加入到定时任务2的延迟队列当中,到时间后自动发布。
对比:实现①是比较简单的实现,一般数据量不大的系统是没有什么太大问题的,实现②将定时任务2的队列设置一个阈值,控制延迟队列中的任务数量,因为延迟队列中需要对新加入的任务进行重排序,所以控制队列任务数量能够保持性能。
具体实现方案:定时任务的核心是利用java自带的定时任务类ScheduledThreadPoolExecutor,其提供了一个方法

/**
     * Creates and executes a one-shot action that becomes enabled
     * after the given delay.
     *
     * @param command the task to execute
     * @param delay the time from now to delay execution
     * @param unit the time unit of the delay parameter
     * @return a ScheduledFuture representing pending completion of
     *         the task and whose {@code get()} method will return
     *         {@code null} upon completion
     * @throws RejectedExecutionException if the task cannot be
     *         scheduled for execution
     * @throws NullPointerException if command is null
     */
    public ScheduledFuture<?> schedule(Runnable command,
                                       long delay, TimeUnit unit);

其中参数command就是需要执行命令delay为延迟时间,unit为时间单位,
新建两个定时任务,分别是定时轮询任务A、定时消息通知任务BA任务每隔2分钟查看B任务中剩余的任务数量,若小于500个,则从数据库取出 适量的 5分钟内要发布的通知,然后将其加入到B任务中,完成定时发布。
简单的demo实现如下(不是实际代码):
Springboot工程结构
springboot定时任务
定时任务配置类 ScheduleConfig.java

package com.yuan.springboot.scheduledemo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;

/**
 * @author luojy
 * @date 2019/2/24
 */
@Configuration
public class ScheduleConfig implements SchedulingConfigurer{

    /**
     * 配置定时轮询任务线程池大小为5
     * @param taskRegistrar
     */
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
    }
    
    /**
     * 设置消息通知任务线程池大小为5
     * @return
     */
    @Bean
    public ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor(){
        return new ScheduledThreadPoolExecutor(5);
    }

}

定时任务A类 ScheduleService.java

package com.yuan.springboot.scheduledemo;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 定时轮询任务
 * @author luojy
 * @date 2019/2/24
 */
@Component
@Slf4j
public class ScheduleService {

    @Autowired
    private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;

    /**
     * 此方法为定时轮询任务
     * 其功能主要是每隔一段时间判断业务用消息通知的定时任务延迟队列剩余任务大小,
     * 可设置阈值,当队列未达到时可从数据库取数据送入队列
     */
    @Scheduled(cron = "*/30 * * * * ?")
    public void test(){
        log.info("30秒钟定时任务执行中...,此时线程名:{}", Thread.currentThread().getName());
        //判断消息通知延迟队列剩余任务个数,若小于100则加入新任务
        if(scheduledThreadPoolExecutor.getQueue().size() < 100){
            for(int i = 0; i < 10; i++){
                addScheduledTask("task" + i);
                log.info("任务task{}进入队列,此时消息通知定时任务队列还剩{}个任务", i, scheduledThreadPoolExecutor.getQueue().size());
            }
        }
    }

    /**
     * 消息通知队列加入新任务
     */
    private void addScheduledTask(String taskName){
        scheduledThreadPoolExecutor.schedule(new ScheduleTask(taskName), 2000, TimeUnit.MILLISECONDS);
    }

}

消息通知任务 ScheduleTask.java

package com.yuan.springboot.scheduledemo;
import lombok.extern.slf4j.Slf4j;
/**
 * 消息通知任务
 * @author luojy
 * @date 2019/2/24
 */
@Slf4j
public class ScheduleTask implements Runnable{

    private String taskName;

    public ScheduleTask(String taskName) {
        this.taskName = taskName;
    }

    @Override
    public void run() {
        log.info("消息通知定时任务{}执行...,当前线程:{}", taskName, Thread.currentThread().getName());
    }
}

启动类SpringbootApplication.java

package com.yuan.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class SpringbootApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringbootApplication.class, args);
	}
}

运行截图:
springboot定时任务
注意事项:
启动类需要添加 @EnableScheduling 注解以打开定时功能

源码: https://github.com/luojinyuan/Demo/tree/master/src/main/java/com/yuan/springboot