Spring 集成任務(wù)調(diào)度功能


來源:https://github.com/dunwu/spring-tutorial
概述
如果想在Spring中使用任務(wù)調(diào)度功能,除了集成調(diào)度框架Quartz這種方式,也可以使用Spring自己的調(diào)度任務(wù)框架。
使用Spring的調(diào)度框架,優(yōu)點是:支持注解@Scheduler,可以省去大量的配置。
實時觸發(fā)調(diào)度任務(wù)
TaskScheduler接口
Spring3引入了TaskScheduler接口,這個接口定義了調(diào)度任務(wù)的抽象方法。
TaskScheduler接口的聲明:
public?interface?TaskScheduler?{
????ScheduledFuture schedule(Runnable task, Trigger trigger);
????ScheduledFuture schedule(Runnable task, Date startTime);
????ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long?period);
????ScheduledFuture scheduleAtFixedRate(Runnable task, long?period);
????ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long?delay);
????ScheduledFuture scheduleWithFixedDelay(Runnable task, long?delay);
}從以上方法可以看出TaskScheduler有兩類重要參數(shù):
- 一個是要調(diào)度的方法,即一個實現(xiàn)了Runnable接口的線程類的run()方法;
- 另一個就是觸發(fā)條件。
DefaultManagedTaskScheduler、ThreadPoolTaskScheduler、TimerManagerTaskScheduler。- DefaultManagedTaskScheduler:基于JNDI的調(diào)度器。
- TimerManagerTaskScheduler:托管
commonj.timers.TimerManager實例的調(diào)度器 - ThreadPoolTaskScheduler:提供線程池管理的調(diào)度器,它也實現(xiàn)了
TaskExecutor接口,從而使的單一的實例可以盡可能快地異步執(zhí)行。
Trigger接口
Trigger接口抽象了觸發(fā)條件的方法。Trigger接口的聲明:public?interface?Trigger?{
????Date nextExecutionTime(TriggerContext triggerContext);
}Trigger接口的實現(xiàn)類CronTrigger:實現(xiàn)了cron規(guī)則的觸發(fā)器類(和Quartz的cron規(guī)則相同)。PeriodicTrigger:實現(xiàn)了一個周期性規(guī)則的觸發(fā)器類(例如:定義觸發(fā)起始時間、間隔時間等)。完整范例
實現(xiàn)一個調(diào)度任務(wù)的功能有以下幾個關(guān)鍵點:(1) 定義調(diào)度器在spring-bean.xml中進行配置使用task:scheduler標簽定義一個大小為10的線程池調(diào)度器,spring會實例化一個ThreadPoolTaskScheduler。xml version="1.0"?encoding="UTF-8"?>
<beans?xmlns="http://www.springframework.org/schema/beans"
???????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
???????xmlns:mvc="http://www.springframework.org/schema/mvc"
???????xmlns:task="http://www.springframework.org/schema/task"
???????xsi:schemaLocation="http://www.springframework.org/schema/beans
???????http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
???????http://www.springframework.org/schema/mvc
???????http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
???????http://www.springframework.org/schema/task
???????http://www.springframework.org/schema/task/spring-task-3.1.xsd">
??<mvc:annotation-driven/>
??<task:scheduler?id="myScheduler"?pool-size="10"/>
beans>注:不要忘記引入xsd:http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.1.xsd(2) 定義調(diào)度任務(wù)定義實現(xiàn)Runnable接口的線程類。import?org.slf4j.Logger;
import?org.slf4j.LoggerFactory;
public?class?DemoTask?implements?Runnable?{
????final?Logger logger = LoggerFactory.getLogger(this.getClass());
????@Override
????public?void?run()?{
????????logger.info("call DemoTask.run");
????}
}(3) 裝配調(diào)度器,并執(zhí)行調(diào)度任務(wù)在一個Controller類中用@Autowired注解裝配TaskScheduler。然后調(diào)動TaskScheduler對象的schedule方法啟動調(diào)度器,就可以執(zhí)行調(diào)度任務(wù)了。@Controller
@RequestMapping("/scheduler")
public class SchedulerController {
????@Autowired
????TaskScheduler scheduler;
????@RequestMapping(value = "/start", method = RequestMethod.POST)
????public void start() {
????????scheduler.schedule(new DemoTask(), new CronTrigger("0/5 * * * * *"));
????}
}訪問/scheduler/start接口,啟動調(diào)度器,可以看到如下日志內(nèi)容:13:53:15.010?myScheduler-1?o.zp.notes.spring.scheduler.DemoTask.run?-?call?DemoTask.run
13:53:20.003?myScheduler-1?o.zp.notes.spring.scheduler.DemoTask.run?-?call?DemoTask.run
13:53:25.004?myScheduler-2?o.zp.notes.spring.scheduler.DemoTask.run?-?call?DemoTask.run
13:53:30.005?myScheduler-1?o.zp.notes.spring.scheduler.DemoTask.run?-?call?DemoTask.run@Scheduler的使用方法
Spring的調(diào)度器一個很大的亮點在于@Scheduler注解,這可以省去很多繁瑣的配置。啟動注解
使用@Scheduler注解先要使用啟動注解開關(guān)。例:xml version="1.0"?encoding="UTF-8"?>
<beans?xmlns="http://www.springframework.org/schema/beans"?
???????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
???????xmlns:mvc="http://www.springframework.org/schema/mvc"?
???????xmlns:task="http://www.springframework.org/schema/task"
???????xsi:schemaLocation="http://www.springframework.org/schema/beans
???????http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
???????http://www.springframework.org/schema/mvc
???????http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
???????http://www.springframework.org/schema/task
???????http://www.springframework.org/schema/task/spring-task-3.1.xsd">
??<mvc:annotation-driven/>
??<task:annotation-driven?executor="myExecutor"?scheduler="myScheduler"/>
??<task:executor?id="myExecutor"?pool-size="5"/>
??<task:scheduler?id="myScheduler"?pool-size="10"/>
beans>@Scheduler定義觸發(fā)條件
例:使用fixedDelay指定觸發(fā)條件為每5000毫秒執(zhí)行一次。注意:必須在上一次調(diào)度成功后的5000秒才能執(zhí)行。@Scheduled(fixedDelay=5000)
public?void?doSomething()?{
????// something that should execute periodically
}例:使用fixedRate指定觸發(fā)條件為每5000毫秒執(zhí)行一次。注意:無論上一次調(diào)度是否成功,5000秒后必然執(zhí)行。@Scheduled(fixedRate=5000)
public?void?doSomething()?{
????// something that should execute periodically
}例:使用initialDelay指定方法在初始化1000毫秒后才開始調(diào)度。@Scheduled(initialDelay=1000, fixedRate=5000)
public?void?doSomething()?{
????// something that should execute periodically
}例:使用cron表達式指定觸發(fā)條件為每5000毫秒執(zhí)行一次。cron規(guī)則和Quartz中的cron規(guī)則一致。@Scheduled(cron="*/5 * * * * MON-FRI")
public?void?doSomething()?{
????// something that should execute on weekdays only
}完整范例
(1) 啟動注解開關(guān),并定義調(diào)度器和執(zhí)行器xml version="1.0"?encoding="UTF-8"?>
<beans?xmlns="http://www.springframework.org/schema/beans"?
???????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
???????xmlns:mvc="http://www.springframework.org/schema/mvc"?
???????xmlns:task="http://www.springframework.org/schema/task"
???????xsi:schemaLocation="http://www.springframework.org/schema/beans
???????http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
???????http://www.springframework.org/schema/mvc
???????http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
???????http://www.springframework.org/schema/task
???????http://www.springframework.org/schema/task/spring-task-3.1.xsd">
??<mvc:annotation-driven/>
??<task:annotation-driven?executor="myExecutor"?scheduler="myScheduler"/>
??<task:executor?id="myExecutor"?pool-size="5"/>
??<task:scheduler?id="myScheduler"?pool-size="10"/>
beans>(2) 使用@Scheduler注解來修飾一個要調(diào)度的方法下面的例子展示了@Scheduler注解定義觸發(fā)條件的不同方式。/**
?* @description?使用@Scheduler注解調(diào)度任務(wù)范例
?* @author?Vicotr Zhang
?* @date?2016年8月31日
?*/
@Component
public?class?ScheduledMgr?{
????private?final?SimpleDateFormat dateFormat = new?SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
????final?Logger logger = LoggerFactory.getLogger(this.getClass());
????/**
?????* 構(gòu)造函數(shù)中打印初始化時間
?????*/
????public?ScheduledMgr()?{
????????logger.info("Current time: {}", dateFormat.format(new?Date()));
????}
????/**
?????* fixedDelay屬性定義調(diào)度間隔時間。調(diào)度需要等待上一次調(diào)度執(zhí)行完成。
?????*/
????@Scheduled(fixedDelay = 5000)
????public?void?testFixedDelay()?throws?Exception {
????????Thread.sleep(6000);
????????logger.info("Current time: {}", dateFormat.format(new?Date()));
????}
????/**
?????* fixedRate屬性定義調(diào)度間隔時間。調(diào)度不等待上一次調(diào)度執(zhí)行完成。
?????*/
????@Scheduled(fixedRate = 5000)
????public?void?testFixedRate()?throws?Exception {
????????Thread.sleep(6000);
????????logger.info("Current time: {}", dateFormat.format(new?Date()));
????}
????/**
?????* initialDelay屬性定義初始化后的啟動延遲時間
?????*/
????@Scheduled(initialDelay = 1000, fixedRate = 5000)
????public?void?testInitialDelay()?throws?Exception {
????????Thread.sleep(6000);
????????logger.info("Current time: {}", dateFormat.format(new?Date()));
????}
????/**
?????* cron屬性支持使用cron表達式定義觸發(fā)條件
?????*/
????@Scheduled(cron = "0/5 * * * * ?")
????public?void?testCron()?throws?Exception {
????????Thread.sleep(6000);
????????logger.info("Current time: {}", dateFormat.format(new?Date()));
????}
}我刻意設(shè)置觸發(fā)方式的間隔都是5s,且方法中均有Thread.sleep(6000);語句。從而確保方法在下一次調(diào)度觸發(fā)時間點前無法完成執(zhí)行,來看一看各種方式的表現(xiàn)吧。啟動spring項目后,spring會掃描@Component注解,然后初始化ScheduledMgr。接著,spring會掃描@Scheduler注解,初始化調(diào)度器。調(diào)度器在觸發(fā)條件匹配的情況下開始工作,輸出日志。截取部分打印日志來進行分析。10:58:46.479?localhost-startStop-1?o.z.n.s.scheduler.ScheduledTasks.<init> -?Current?time: 2016-08-31?10:58:46
10:58:52.523?myScheduler-1?o.z.n.s.scheduler.ScheduledTasks.testFixedRate?-?Current?time: 2016-08-31?10:58:52
10:58:52.523?myScheduler-3?o.z.n.s.scheduler.ScheduledTasks.testFixedDelay?-?Current?time: 2016-08-31?10:58:52
10:58:53.524?myScheduler-2?o.z.n.s.scheduler.ScheduledTasks.testInitialDelay?-?Current?time: 2016-08-31?10:58:53
10:58:55.993?myScheduler-4?o.z.n.s.scheduler.ScheduledTasks.testCron?-?Current?time: 2016-08-31?10:58:55
10:58:58.507?myScheduler-1?o.z.n.s.scheduler.ScheduledTasks.testFixedRate?-?Current?time: 2016-08-31?10:58:58
10:58:59.525?myScheduler-5?o.z.n.s.scheduler.ScheduledTasks.testInitialDelay?-?Current?time: 2016-08-31?10:58:59
10:59:03.536?myScheduler-3?o.z.n.s.scheduler.ScheduledTasks.testFixedDelay?-?Current?time: 2016-08-31?10:59:03
10:59:04.527?myScheduler-1?o.z.n.s.scheduler.ScheduledTasks.testFixedRate?-?Current?time: 2016-08-31?10:59:04
10:59:05.527?myScheduler-4?o.z.n.s.scheduler.ScheduledTasks.testInitialDelay?-?Current?time: 2016-08-31?10:59:05
10:59:06.032?myScheduler-2?o.z.n.s.scheduler.ScheduledTasks.testCron?-?Current?time: 2016-08-31?10:59:06
10:59:10.534?myScheduler-9?o.z.n.s.scheduler.ScheduledTasks.testFixedRate?-?Current?time: 2016-08-31?10:59:10
10:59:11.527?myScheduler-10?o.z.n.s.scheduler.ScheduledTasks.testInitialDelay?-?Current?time: 2016-08-31?10:59:11
10:59:14.524?myScheduler-4?o.z.n.s.scheduler.ScheduledTasks.testFixedDelay?-?Current?time: 2016-08-31?10:59:14
10:59:15.987?myScheduler-6?o.z.n.s.scheduler.ScheduledTasks.testCron?-?Current?time: 2016-08-31?10:59:15構(gòu)造方法打印一次,時間點在10:58:46。testFixedRate打印四次,每次間隔6秒。說明,fixedRate不等待上一次調(diào)度執(zhí)行完成,在間隔時間達到時立即執(zhí)行。testFixedDelay打印三次,每次間隔大于6秒,且時間不固定。說明,fixedDelay等待上一次調(diào)度執(zhí)行成功后,開始計算間隔時間,再執(zhí)行。testInitialDelay第一次調(diào)度時間和構(gòu)造方法調(diào)度時間相隔7秒。說明,initialDelay在初始化后等待指定的延遲時間才開始調(diào)度。testCron打印三次,時間間隔并非5秒或6秒,顯然,cron等待上一次調(diào)度執(zhí)行成功后,開始計算間隔時間,再執(zhí)行。此外,可以從日志中看出,打印日志的線程最多只有10個,說明2.1中的調(diào)度器線程池配置生效。- End -

技術(shù)連載目錄(可點擊跳轉(zhuǎn)即可閱讀):
面試題系列教程??點擊-->?面試題技術(shù)干貨連載目錄?跳轉(zhuǎn)
Maven系列教程??點擊-->?Maven技術(shù)干貨連載目錄?跳轉(zhuǎn)
MyBatis系列教程??點擊-->?MyBatis技術(shù)干貨連載目錄?跳轉(zhuǎn)
JVM調(diào)優(yōu)總結(jié)系列教程??點擊-->?JVM調(diào)優(yōu)技術(shù)干貨連載目錄?跳轉(zhuǎn)
點擊在看,愿你我不再陌生?![]()
評論
圖片
表情
