SpringBoot 【 第三章 】定时管理

2019-01-25  本文已影响0人  陈兄
image

Schedule定时器

定时任务想必是每个开发人员都必须要操作的。同步,异步方式密切的与业务相关。

在Spring Boot启动类中加入@EnableScheduling注解,启用定时任务的配置

  /**
  * Created by cw on 2017/10/25. good day.
  *
  * @Author: Chen Wu
  * Blog: http://www.updatecg.xin
  */
  @EnableEurekaServer
  @SpringBootApplication
  @RestController
  @EnableScheduling
  @EnableConfigurationProperties
  public class EureKaServer extends SpringBootServletInitializer implements CommandLineRunner {

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

定时任务类

  /**
  * Created by cw on 2017/10/25. good day.
  *
  * @Author: Chen Wu
  * Blog: http://www.updatecg.xin
  */
  @Component
  public class FileScheduled {
    private static Logger log = Logger.getLogger(FileScheduled.class);
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

    @Scheduled(cron = @Scheduled(cron="0 00 2 ? * *")
    private void deleteFile() {
        try {
            String path = String.join("/", System.getProperty("user.dir"), "tmp");
            FileUtil.cleanDir(path);
            log.info("Remove file success . data = " + dateFormat.format(new Date()));
        } catch (Exception e) {
            log.info("Remove file fail . data = " + dateFormat.format(new Date()));
            e.printStackTrace();
        }
    }
  }

每天凌晨2点执行清除文件内容。

@Scheduled详解

SpringBoot @Scheduled 和spring相似。

写法 含有
@Scheduled(fixedRate = 60*1000) 固定每分钟执行一次
@Scheduled(fixedDelay = 60*1000) 上次任务结束后一分钟后再次执行
@Scheduled(cron=" 0 10,14,16 * * ?") 每天上午10点,下午2点,4点
@Scheduled(cron=" 0/30 9-17 * * ?") 朝九晚五工作时间内每半小时
@Scheduled(cron=" 0 12 ? * WED") 表示每个星期三中午12点
@Scheduled(cron=" 0 12 * * ?") 每天中午12点触发
@Scheduled(cron=" 15 10 ? * * ") 每天上午10:15触发
@Scheduled(cron=" 15 10 * * ? ") 每天上午10:15触发
@Scheduled(cron=" 15 10 * * ? * ") 每天上午10:15触发
@Scheduled(cron=" 15 10 * * ? 2005") 2005年的每天上午10:15触发
@Scheduled(cron=" * 14 * * ?") 在每天下午2点到下午2:59期间的每1分钟触发
@Scheduled(cron=" 0/5 14 * * ?") 在每天下午2点到下午2:55期间的每5分钟触发
@Scheduled(cron=" 0/5 14,18 * * ?") 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
@Scheduled(cron=" 0-5 14 * * ?") 在每天下午2点到下午2:05期间的每1分钟触发
@Scheduled(cron=" 10,44 14 ? 3 WED") 每年三月的星期三的下午2:10和2:44触发
@Scheduled(cron=" 15 10 ? * MON-FRI") 周一至周五的上午10:15触发
@Scheduled(cron=" 15 10 15 * ?") 每月15日上午10:15触发
@Scheduled(cron=" 15 10 L * ?") 每月最后一日的上午10:15触发
@Scheduled(cron=" 15 10 ? * 6L") 每月的最后一个星期五上午10:15触发
@Scheduled(cron=" 15 10 ? * 6L 2002-2005") 2002年至2005年的每月的最后一个星期五上午10:15触发
@Scheduled(cron=" 15 10 ? * 6#3") 每月的第三个星期五上午10:15触发

同步调用

定义Task类,创建三个处理函数分别模拟三个执行任务的操作,操作消耗时间随机取(10秒内)

  package com.androidmov.adManagement.maker.scheduled;

  import org.apache.log4j.Logger;
  import org.springframework.stereotype.Component;

  import java.util.Random;

  /**
  * Created by cw on 2017/10/27. good day.
  *
  * @Author: Chen Wu
  * Blog: http://www.updatecg.xin
  */
  @Component
  public class Task {
    private static Logger log = Logger.getLogger(Task.class);
    public static Random random =new Random();

    public void taskOne() throws InterruptedException {
        log.info("任务一开始执行...");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务一,耗时:" + (end - start) + "毫秒");
    }

    public void taskTwo() throws InterruptedException {
        log.info("任务二开始执行...");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务二,耗时:" + (end - start) + "毫秒");
    }

    public void taskThreee() throws InterruptedException {
        log.info("任务三开始执行...");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务一,耗时:" + (end - start) + "毫秒");
    }
  }

容器启动调用此三个任务,执行结果是依次按照顺序执行。

  @Override
  public void run(String... strings) throws Exception {
    System.out.println("=========Application init============");

    task.taskOne();
    task.taskTwo();
    task.taskThreee();
  }

执行结果:

  [No TaskScheduler/ScheduledExecutorService bean found for scheduled processing]
  [Tomcat started on port(s): 8093 (http)]
  [Updating port to 8093]
  =========Application init============
  [任务一开始执行...]
  [完成任务一,耗时:9821毫秒]
  [任务二开始执行...]
  [完成任务二,耗时:1043毫秒]
  [任务三开始执行...]
  [完成任务一,耗时:9580毫秒]
  [Started EureKaServer in 31.177 seconds (JVM running for 31.96)]

结果显然按照任务一、任务二、任务三顺序的执行。

异步调用/回调

从上面可见同步调用是按照顺序来执行的,但是执行时间比较长。若这三个任务本身之间不存在依赖关系,可以并发执行的话,同步调用在执行效率方面就比较差,可以考虑通过异步调用的方式来并发执行。

调用

在Spring Boot中,我们只需要通过使用@Async注解就能简单的将原来的同步函数变为异步函数。

  package com.androidmov.adManagement.maker.scheduled;

  import org.apache.log4j.Logger;
  import org.springframework.scheduling.annotation.Async;
  import org.springframework.stereotype.Component;

  import java.util.Random;

  /**
  * Created by cw on 2017/10/27. good day.
  *
  * @Author: Chen Wu
  * Blog: http://www.updatecg.xin
  */
  @Component
  public class AsynTask {
    private static Logger log = Logger.getLogger(AsynTask.class);
    public static Random random =new Random();

    @Async
    public void asynTaskOne() throws InterruptedException {
        log.info(" 任务一开始执行...");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务一,耗时:" + (end - start) + "毫秒");
    }
    @Async
    public void asynTaskTwo() throws InterruptedException {
        log.info(" 任务二开始执行...");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务二,耗时:" + (end - start) + "毫秒");
    }
    @Async
    public void asynTaskThreee() throws InterruptedException {
        log.info(" 任务三开始执行...");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("完成任务一,耗时:" + (end - start) + "毫秒");
    }
  }

为了让@Async注解能够生效,还需要在Spring Boot的主程序中配置@EnableAsync,如下所示:

  @EnableAsync
  public class EureKaServer extends SpringBootServletInitializer implements CommandLineRunner {

    @Autowired
    private SynTask task;

    public static void main(String[] args) {
        createLocalTempDirectory();
        SpringApplication.run(EureKaServer.class, args);
    }
    @Override
    public void run(String... strings) throws Exception {
        System.out.println("=========Application init============");

        task.taskOne();
        task.taskTwo();
        task.taskThreee();
    }
  }

<strong>注: @Async所修饰的函数不要定义为static类型,这样异步调用不会生效.</strong>

回调

假设我们需要统计一下三个任务并发执行共耗时是多少。

此时回调需要Future<T>来返回异步调用的结果。

  @Async
  public Future<String> taskOne() throws InterruptedException {
      log.info("任务一开始执行...");
      long start = System.currentTimeMillis();
      Thread.sleep(random.nextInt(10000));
      long end = System.currentTimeMillis();
      log.info("完成任务一,耗时:" + (end - start) + "毫秒");
      return new AsyncResult<>("任务一完成");
  }

此时任务所消耗的时候明显比同步消耗的时间少。也不会去等待其他任务响应后再去执行。

  [Updating port to 8093]
  =========Application init============
  [No task executor bean found for async processing: no bean of type TaskExecutor and no bean named 'taskExecutor' either]
  [任务一开始执行...]
  [任务三开始执行...]
  [任务二开始执行...]
  [完成任务二,耗时:2540毫秒]
  [完成任务三,耗时:2684毫秒]
  [完成任务一,耗时:3486毫秒]
  [任务全部完成,总耗时:4015毫秒]
  [Started EureKaServer in 20.461 seconds (JVM running for 21.3)]

<strong>对应代码分享在 [https://github.com/UpdateCw/SpringBoot]</strong>

上一篇下一篇

猜你喜欢

热点阅读