Quartz定時任務入門

1.Quartz是什麼

大部分公司都會用到定時任務這個功能。
拿火車票購票來說,當你下單後,後臺就會插入一條待支付的task(job),一般是30分鐘,超過30min後就會執行這個job,去判斷你是否支付,未支付就會取消此次訂單;當你支付完成之後,後臺拿到支付回調後就會再插入一條待消費的task(job),Job觸發日期爲火車票上的出發日期,超過這個時間就會執行這個job,判斷是否使用等。

在我們實際的項目中,當Job過多的時候,肯定不能人工去操作,這時候就需要一個任務調度框架,幫我們自動去執行這些程序。那麼該如何實現這個功能呢?

在這裏插入圖片描述

  • Quartz是OpenSymphony開源組織在Job scheduling領域又一個開源項目,它可以與J2EE與J2SE應用程序相結合也可以單獨使用。
  • 在企業級應用中,經常會制定一些“計劃任務”,即在某個時間點做某件事情,核心是以時間爲關注點,即在一個特定的時間點,系統執行指定的一個操作
  • 任務調度涉及多線程併發、線程池維護、運行時間規則解析、運行現場的保護以恢復等方面
  • Quartz框架是一個開源的企業級任務調度服務,已經被作爲任務調度的良好解決方案
  • Quartz對任務調度進行了高度抽象,提出了3個核心概念,並在org.quartz包中通過接口和類進行了描述。

2.Quartz能幹什麼?

  • 場景 #1: 下載交易流水
    從銀行下載當日交易流水

  • 場景 #2: 郵件提醒和告警
    公司出於安全考慮,讓每個員工三個月換一次郵箱密碼。這種情況下,可以創建一個作業,讓它每天午夜運行一次,並且向離過期時間不到三天的所有用戶發郵件提醒。這裏可以恰到好處的用到作業調度器。圖 1.1 描繪了密碼這個提醒作業。
    在這裏插入圖片描述
    圖 1.1 密碼過期的作業每晚發送郵件給密碼很快會過期的用戶

3.Quartz核心概念

  • 任務:就是執行的工作內容。Quartz提供Job接口來支持任務定義
  • 觸發器(何時觸發的規則):定義觸發Job執行的時間觸發規則。Quartz提供Trigger類及其子類支持觸發器功能
  • 調度器:Quartz提供了Scheduler接口,將工作任務和觸發器綁定,保證任務可以在正確的時間執行

在這裏插入圖片描述

4.Quartz下載

下載:http://www.quartz-scheduler.org/download/index.html

  • docs/wikidocs
    Quartz 的幫助文件
  • docs/api
    Quartz 框架的JavaDoc Api 說明文檔
  • Examples
    Quartz 的例子
  • Lib
    Quartz 使用到的第三方包
  • src
    源碼

5.HelloWorld程序

  • 創建maven工程,導入依賴
<dependency>
	<groupId>org.quartz-scheduler</groupId>
	<artifactId>quartz</artifactId>
	<version>2.2.1</version>
</dependency>
<dependency>
	<groupId>org.quartz-scheduler</groupId>
	<artifactId>quartz-jobs</artifactId>
	<version>2.2.1</version>
</dependency>

一個最簡單的應用包含兩個類:

  • HelloJob.java
    任務類,需要實現 job 接口,裏面實現了你想要 定時執行的方法
  • SimpleExample.java
    主線程,用於任務的調度,執行等操作.

上代碼:

  • HelloJob.java
/**
 * Description: 打印任意內容
 */
public class HelloJob 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("HelloJob start at:" + printTime + ", prints: Hello Job-" + new Random().nextInt(100));

    }
}

任務很簡單,HelloJob 實現了Quartz 的Job接口中的execute方法,任務的內容就是打印一行字。具體這個任務應該何時去執行,都在SimpleExample.java裏面去定義實現。

  • SimpleExample.java
public class SimpleExample{

    public static void main(String[] args) throws SchedulerException, InterruptedException {
        // 1、創建調度器Scheduler
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        // 2、創建JobDetail實例,並與PrintWordsJob類綁定(Job執行內容)
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                                        .withIdentity("job1", "group1").build();
        // 3、構建Trigger實例,每隔1s執行一次
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
                .startNow()//立即生效
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(1)//每隔1s執行一次
                .repeatForever()).build();//一直執行

        //4、執行
        scheduler.scheduleJob(jobDetail, trigger);
        System.out.println("--------scheduler start ! ------------");
        scheduler.start();

        //睡眠
        TimeUnit.MINUTES.sleep(1);
        scheduler.shutdown();
        System.out.println("--------scheduler shutdown ! ------------");
    }
}


通過代碼可以看到幾個重要的類:

  • JobDetail: 真正的任務內容,任務本身是集成Job接口的,但是真正的任務是JobBuilder通過反射的方式實例化的,
  • Trigger: 觸發器,定義任務應當開始的時間,主要分爲兩類SimpleTrigger,CronTrigger,當前例子的就是簡單觸發器,CronTrigger主要用於處理quartz表達式定義的任務,比如每個月20號,每個星期一之類的。
  • Scheduler: 計劃執行者,現在我們有了要做的內容(HelloJob),有了要做的時間(下一分鐘),接下來,就把這兩個內容填充到計劃任務Scheduler對象裏面,到了時間它就可以自動運行了.

6.Quart架構

當要深入研究一個技術時,研究它的體系結構和內部運行原理,不失爲一種較好的方式。同理,我們在研究Quartz時,也採用類似的方法,

下圖爲Quartz的大致結構圖。
在這裏插入圖片描述

6.1.Quartz關鍵組件

Quartz比較關鍵的兩個核心組件分別爲Job和Trigger
job–表示任務是什麼
trigger–表示何時觸發任務
在這裏插入圖片描述

JobExecutionContext
JobExecutionContext中包含了Quartz運行時的環境以及Job本身的詳細數據信息。
當Schedule調度執行一個Job的時候,就會將JobExecutionContext傳遞給該Job的execute()中,Job就可以通過JobExecutionContext對象獲取信息。
主要信息有
在這裏插入圖片描述
JobDataMap
JobDataMap實現了JDK的Map接口,可以以Key-Value的形式存儲數據。
JobDetail、Trigger都可以使用JobDataMap來設置一些參數或信息,
Job執行execute()方法的時候,JobExecutionContext可以獲取到JobExecutionContext中的信息:
如:

 // 2、創建JobDetail實例,並與PrintWordsJob類綁定(Job執行內容)
 JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).usingJobData("jobDetail1", "這個Job用來測試的")
         .withIdentity("job1", "group1").build();

 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
         .usingJobData("trigger1", "這是jobDetail1的trigger")
         .startNow()//立即生效
         .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                 .withIntervalInSeconds(1)//每隔1s執行一次
                 .repeatForever()).build();//一直執行

Job執行的時候,可以獲取到這些參數信息:

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        System.out.println(jobExecutionContext.getJobDetail().getJobDataMap().get("jobDetail1"));
        System.out.println(jobExecutionContext.getTrigger().getJobDataMap().get("trigger1"));
        String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date());
        System.out.println("HelloJobstart at:" + printTime + ", prints: Hello Job-" + new Random().nextInt(100));

    }

6.Trigger、SimpleTrigger、CronTrigger

在這裏插入圖片描述

Trigger
Trigger是Quartz的觸發器,會去通知Scheduler何時去執行對應Job。

new Trigger().startAt():表示觸發器首次被觸發的時間;
new Trigger().endAt():表示觸發器結束觸發的時間;

SimpleTrigger
SimpleTrigger可以實現在一個指定時間段內執行一次作業任務或一個時間段內多次執行作業任務。
下面的程序就實現了程序運行5s後開始執行Job,執行Job 5s後結束執行:

Date startDate = new Date();
startDate.setTime(startDate.getTime() + 5000);

 Date endDate = new Date();
 endDate.setTime(startDate.getTime() + 5000);

        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
                .usingJobData("trigger1", "這是jobDetail1的trigger")
                .startNow()//立即生效
                .startAt(startDate)
                .endAt(endDate)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(1)//每隔1s執行一次
                .repeatForever()).build();//一直執行

6.1 CronTrigger介紹

CronTrigger也是Trigger的子類
在這裏插入圖片描述
CronTrigger功能非常強大,是基於日曆的作業調度,而SimpleTrigger是精準指定間隔,所以相比SimpleTrigger,CroTrigger更加常用。CroTrigger是基於Cron表達式的,先了解下Cron表達式:
由7個子表達式組成字符串的。

CronTrigger和SimpleTrigger的對比
在這裏插入圖片描述

CronTrigger允許用戶更精準地控制任務的運行日期和時間,而不僅僅是定義工作的頻度
CronTrigger通過Cron表達式定義準確的運行時間點。創建CronTrigger的語法如下:
創建CronTrigger的語法很簡單,最關鍵的是Cron表達式的編寫

要使用CronTrigger,必須掌握Cron表達式
在這裏插入圖片描述
Cron表達式由6~7個由空格分隔的時間元素組成。第7個元素可選
在這裏插入圖片描述
在這裏插入圖片描述
Cron表達式的每個字段,都可以顯式地規定一個值(如49)、一個範圍(如1-6)、一個列表(如1,3,5)或者一個通配符(如*)

Cron表達式有幾個特殊的字符,說明如下
“ - ”:中劃線,表示一個範圍 1-10
“ , ”:使用逗號間隔的數據,表示一個列表 1,8,10
“ * ”:表示每一個值,它可以用於所有字段。例如:在小時字段表示每小時 *
“ ? ”:該字符僅用於“月份中的哪一天”字段和“星期幾”字段,表示不指定值
“ / ”:通常表示爲x/y,x爲起始值,y表示值的增量。
“ L ”:表示最後一天,僅在日期和星期字段中使用
“ # ”:只能用於“星期幾”字段,表示這個月的第幾個周幾。例如:“6#3”指這個月第三個週五

6.2 Cron表達式示例

語法格式:

秒 分 時 天 月 星期 年

常用表達式例子
(1)0 0 2 1 * ? * 表示在每月的1日的凌晨2點調整任務
(2)0 15 10 ? * MON-FRI 表示週一到週五每天上午10:15執行作業
(3)0 15 10 ? 6L 2002-2006 表示2002-2006年的每個月的最後一個星期五上午10:15執行作
(4)0 0 10,14,16 * * ? 每天上午10點,下午2點,4點
(5)0 0/30 9-17 * * ? 朝九晚五工作時間內每半小時
(6)0 0 12 ? * WED 表示每個星期三中午12點
(7)0 0 12 * * ? 每天中午12點觸發
(8)0 15 10 ? * * 每天上午10:15觸發
(9)0 15 10 * * ? 每天上午10:15觸發
(10)0 15 10 * * ? * 每天上午10:15觸發
(11)0 15 10 * * ? 2005 2005年的每天上午10:15觸發
(12)0 * 14 * * ? 在每天下午2點到下午2:59期間的每1分鐘觸發
(13)0 0/5 14 * * ? 在每天下午2點到下午2:55期間的每5分鐘觸發
(14)0 0/5 14,18 * * ? 在每天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發
(15)0 0-5 14 * * ? 在每天下午2點到下午2:05期間的每1分鐘觸發
(16)0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44觸發
(17)0 15 10 ? * MON-FRI 週一至週五的上午10:15觸發
(18)0 15 10 15 * ? 每月15日上午10:15觸發
(19)0 15 10 L * ? 每月最後一日的上午10:15觸發
(20)0 15 10 ? * 6L 每月的最後一個星期五上午10:15觸發
(21)0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最後一個星期五上午10:15觸發
(22)0 15 10 ? * 6#3 每月的第三個星期五上午10:15觸發

在這裏插入圖片描述

Cron表達式在線插件:http://cron.qqe2.com/
在這裏插入圖片描述

6.3 Cron表達式案例

下面的代碼就實現了每週一到週五上午10:30執行定時任務

public class Test3 {

    public static void main(String[] args) throws SchedulerException, InterruptedException {
        // 1、創建調度器Scheduler
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        // 2、創建JobDetail實例,並與PrintWordsJob類綁定(Job執行內容)
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .usingJobData("jobDetail1", "這個Job用來測試的")
                .withIdentity("job1", "group1").build();
        // 3、構建Trigger實例,每隔1s執行一次
        Date startDate = new Date();
        startDate.setTime(startDate.getTime() + 5000);

        Date endDate = new Date();
        endDate.setTime(startDate.getTime() + 5000);

        CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
                .usingJobData("trigger1", "這是jobDetail1的trigger")
                .startNow()//立即生效
                .startAt(startDate)
                .endAt(endDate)
                .withSchedule(CronScheduleBuilder.cronSchedule("* 30 10 ? * 1/5 2018"))
                .build();

        //4、執行
        scheduler.scheduleJob(jobDetail, cronTrigger);
        System.out.println("--------scheduler start ! ------------");
        scheduler.start();
        System.out.println("--------scheduler shutdown ! ------------");

    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章