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>