點贊多大膽,就有多大産!路漫漫其修遠兮,吾将上下而求索,開源促使進步,獻給每一位技術使用者和愛好者!
幹貨滿滿,擺好姿勢,點贊發車,學習知識
定時任務介紹
什麼是定時任務
大部分項目都會使用到定時任務這個功能,拿商城訂單來說,當你下單之後如果沒有付款,背景就會插入一條待支付的task(job),一般是30分鐘,超過30min後就會執行這個job,去判斷你是否支付,如果30分鐘後沒有支付則取消這個訂單,定時任務在項目中使用場景非常多,比如:郵件定時發送,優惠券到期提醒等等場景。
常用定時工具
Java 的Timer
這是java自帶的java.util.Timer類,這個類允許你排程一個java.util.TimerTask任務。使用這種方式可以讓你的程式按照某一個頻度執行,但不能在指定時間運作。一般用的較少
Spring Task
Spring3.0以後自帶的task,可以将它看成一個輕量級的Quartz,而且使用起來比Quartz簡單許多
Quartz
這是一個功能比較強大的的排程器,可以讓你的程式在指定時間執行,也可以按照某一個頻度執行,用的比較多的定時任務工具
Quartz
什麼是Quartz
Quartz是OpenSymphony開源組織在Job scheduling領域又一個開源項目。
Quartz 是一個完全由 Java 編寫的開源作業排程架構,為在 Java 應用程式中進行作業排程提供了簡單卻強大的機制
Quartz 可以與 J2EE 與 J2SE 應用程式相結合也可以單獨使用。
Quartz 允許程式開發人員根據時間的間隔來排程作業。
Quartz 實作了作業和觸發器的多對多的關系,還能把多個作業與不同的觸發器關聯。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL0EEVPJzZ61kMNpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLzUTN3MTNyIjM0ETMxkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
Quartz核心概念
我們需要明白 Quartz 的幾個核心概念,這樣了解起 Quartz 的原理就會變得簡單了。
- Job 表示一個工作,要執行的具體内容。此接口中隻有一個方法,如下:
void execute(JobExecutionContext context)
- JobDetail 表示一個具體的可執行的排程程式,Job 是這個可執行程排程程式所要執行的内容,另外 JobDetail 還包含了這個任務排程的方案和政策。
- Trigger 代表一個排程參數的配置,什麼時候去調。包括SimpleTrigger和CronTrigger
- Scheduler 代表一個排程容器,一個排程容器中可以注冊多個 JobDetail 和 Trigger。當 Trigger 與 JobDetail 組合,就可以被 Scheduler 容器排程了
- 是以我們使用Quartz時需要建立一個Job、使用Trigger來定義調用時間和政策、通過Scheduler來排程任務。
Quartz運作環境
- Quartz 可以運作嵌入在另一個獨立式應用程式。
- Quartz 可以在應用程式伺服器(或 servlet 容器)内被執行個體化,并且參與 XA 事務。
- Quartz 可以作為一個獨立的程式運作(其自己的 Java 虛拟機内),可以通過 RMI 使用。
- Quartz 可以被執行個體化,作為獨立的項目叢集(負載平衡和故障轉移功能),用于作業的執行
Quartz入門案例
我們這裡的案例每1秒調用一次任務,執行1分鐘後結束
導入依賴
<!--quzrtz依賴-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
建立Job
package com.stt.springbootquartz.job;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* ClassName: PrintWordJob
* Description: 列印資料
* date: 2019/11/14 0014 下午 22:40
*
* @author stt
* @since JDK 1.8
*/
public class PrintWordJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date());
//列印時間
System.out.println(printTime);
}
}
建立Schedule
package com.stt.springbootquartz.schedule;
import com.stt.springbootquartz.job.PrintWordJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.util.concurrent.TimeUnit;
/**
* ClassName: MyScheduler
* Description:
* date: 2019/11/14 0014 下午 22:43
*
* @author stt
* @since JDK 1.8
*/
public class MyScheduler {
public static void main(String[] args) throws SchedulerException, InterruptedException {
//1、建立排程器Scheduler
StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
//2、建立JobDetail執行個體、b并與PrintWordJob綁定
JobDetail jobDetail = JobBuilder.newJob(PrintWordJob.class).withIdentity("job1", "group1").build();
//3、建立Trigger執行個體,每隔1秒執行一次
SimpleTrigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
.startNow()//立即生效
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1)//每秒執行一次
.repeatForever())//一直執行
.build();
//4、執行
scheduler.scheduleJob(jobDetail,trigger);
System.out.println("排程器開始");
scheduler.start();
//睡眠
TimeUnit.MINUTES.sleep(1);
scheduler.shutdown();
System.out.println("排程器關閉");
}
}
Quartz API
描述
Quartz API的關鍵接口是:
- Scheduler - 與排程程式互動的主要API。
- Job - 由希望由排程程式執行的元件實作的接口。
- JobDetail - 用于定義作業的執行個體。
- Trigger(即觸發器) - 定義執行給定作業的計劃的元件。
- JobBuilder - 用于定義/建構JobDetail執行個體,用于定義作業的執行個體。
- TriggerBuilder - 用于定義/建構觸發器執行個體。
Scheduler的生命期,從SchedulerFactory建立它時開始,到Scheduler調用shutdown()方法時結束;Scheduler被建立後,可以增加、删除和列舉Job和Trigger,以及執行其它與排程相關的操作(如暫停Trigger)。但是,Scheduler隻有在調用start()方法後,才會真正地觸發trigger(即執行job)。
Job和JobDetail
可以看到,我們傳給scheduler一個JobDetail執行個體,因為我們在建立JobDetail時,将要執行的job的類名傳給了JobDetail,是以scheduler就知道了要執行何種類型的job;每次當scheduler執行job時,在調用其execute(…)方法之前會建立該類的一個新的執行個體;執行完畢,對該執行個體的引用就被丢棄了,執行個體會被垃圾回收;這種執行政策帶來的一個後果是,job必須有一個無參的構造函數(當使用預設的JobFactory時);另一個後果是,在job類中,不應該定義有狀态的資料屬性,因為在job的多次執行中,這些屬性的值不會保留。那麼如何給job執行個體增加屬性或配置呢?如何在job的多次執行中,跟蹤job的狀态呢?答案就是:JobDataMap,JobDetail對象的一部分
為什麼設計成JobDetail + Job,不直接使用Job,因為JobDetail定義的是任務資料,而真正的執行邏輯是在Job中。這是因為任務是有可能并發執行,如果Scheduler直接使用Job,就會存在對同一個Job執行個體并發通路的問題。而JobDetail & Job 方式,Sheduler每次執行,都會根據JobDetail建立一個新的Job執行個體,這樣就可以規避并發通路的問題。
JobDataMap
JobDataMap中可以包含不限量的(序列化的)資料對象,在job執行個體執行的時候,可以使用其中的資料;JobDataMap是Java Map接口的一個實作,額外增加了一些便于存取基本類型的資料的方法。将job加入到scheduler之前,在建構JobDetail時,可以将資料放入JobDataMap,如下示例:
JobExecutionContext
JobExecutionContext中包含了Quartz運作時的環境以及Job本身的詳細資料資訊。當Schedule排程執行一個Job的時候,就會将JobExecutionContext傳遞給該Job的execute()中,Job就可以通過JobExecutionContext對象擷取資訊
Trigger
Trigger是Quartz的觸發器,會去通知Scheduler何時去執行對應Job。
new Trigger().startAt():表示觸發器首次被觸發的時間;
new Trigger().endAt():表示觸發器結束觸發的時間;
SimpleTrigger
SimpleTrigger可以實作在一個指定時間段内執行一次作業任務或一個時間段内多次執行作業任務。下面提供幾個案例
指定時間開始觸發,不重複
SimpleTrigger trigger = (SimpleTrigger) newTrigger()
.withIdentity("trigger1", "group1")
.startAt(myStartTime) // 定義時間
.forJob("job1", "group1")
.build();
指定時間觸發,每隔10秒執行一次,重複10次
trigger = newTrigger()
.withIdentity("trigger3", "group1")
.startAt(myTimeToStartFiring) // i如果沒有給出開始時間(如果忽略了這一行),則暗示“now”
.withSchedule(simpleSchedule()
.withIntervalInSeconds(10)
.withRepeatCount(10)) // 10次重複将産生11次發射
.forJob(myJob) //标記作業
.build();
5分鐘以後開始觸發,僅執行一次:
trigger = (SimpleTrigger) newTrigger()
.withIdentity("trigger5", "group1")
.startAt(futureDate(5, IntervalUnit.MINUTE)) // 使用DateBuilder在将來建立一個日期
.forJob(myJobKey)
.build();
立即觸發,每個5分鐘執行一次,直到22:00:
trigger = newTrigger()
.withIdentity("trigger7", "group1")
.withSchedule(simpleSchedule()
.withIntervalInMinutes(5)
.repeatForever())
.endAt(dateOf(22, 0, 0))
.build();
建立一個觸發器,将在下一個小時的整點觸發,然後每2小時重複一次:
trigger = newTrigger()
.withIdentity("trigger8")
.startAt(evenHourDate(null))
.withSchedule(simpleSchedule()
.withIntervalInHours(2)
.repeatForever())
.build();
scheduler.scheduleJob(trigger, job);
SpringBoot整合Quartz
pom依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
任務類
public class TestJob extends QuartzJobBean {
private static final Logger log = LoggerFactory.getLogger(TestJob.class);
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info("調用。。。。。。。。。");
}
}
定時任務配置類
@Configuration
public class QuartzConfiguration {
@Bean
public JobDetail testJobDetail(){
return JobBuilder.newJob(TestJob.class).withIdentity("testJob")
.storeDurably().build();
}
@Bean
public Trigger testTrigger(){
//設定定時政策
SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(2).repeatForever();
//建立Trigger對象
return TriggerBuilder.newTrigger().
forJob(testJobDetail()).withIdentity("testTrigger").withSchedule(scheduleBuilder).build();
}
}
到這裡大家啟動SpringBoot項目,控制台每2秒就會輸出一個 調用。。。