最牛逼的任務排程工具 | Quartz
Quartz 是一個完全由 Java 編寫的開源作業排程框架,不要讓作業排程這個術語嚇著你,其實不難。儘管 Quartz 框架整合了許多額外功能,但就我們使用來說,你會發現它易用得簡直讓人受不了!
簡單來說,任務排程就是在指定時間做指定的事,之前說過在執行定時定頻率作業時可以使用原生 JDK,Timer 和 TimerTask 。
但是假如我們有一些非常苛刻的要求該怎麼辦呢?比如,在每年 5 月的第二個星期日和每年 6 月的第三個星期日給我發一個郵件。
這種看似隨機但還有有一點規律的定時任務該怎麼實現呢?別急,Quartz 都能給你搞定!
我們執行任務必須的幾個條件,一個是排程器,二是執行的任務,三是觸發器(可以理解為設定鬧鐘)。連在一起就是我們使用排程器將任務和觸發器繫結在一起執行,以達到在指定的時間點執行或迴圈執行任務。
雖說 Quartz 框架有十多個包,300 多個類,但是我們使用它還是比較容易的。
首先,建立一個任務,一樣的套路我們只需要實現 Job 類,實現特定的方法即可,任務建立成功。
public class HelloJob implements Job{ @Override public void execute(JobExecutionContext context) throws JobExecutionException { TimeUtil.printCurrentTime(); System.out.println("Hello world!"); } }
但是我們排程器繫結的不是 Job 的實現類 HelloJob,而是一個 JobDetail,為什麼需要它呢,都寫在臉上了,Job 詳情嘛!
我們執行任務需要知道任務的名稱啊,分組之類的,這樣打出來的 log 也好分辨。這個 JobDetail 是和哪個 Job 繫結的呢?我們還要知道 Class 資訊。在任務執行的時候可能還需要傳參,這些都會封裝在 JobDetail 中。
JobDetail 的建立就更是巧妙了,Quartz 為任務和觸發器的建立提供了一種構建器風格的 API。具體的建立就是這樣(一路點下去)
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build();
呃呃,構建器風格…… 我也不是很懂,感覺用起來很方便就對了,這樣我們就建立了一個 JobDetail ,並設定了繫結的 Job (HelloJob),還為任務命名並分組,其實還可以繼續繫結引數。這裡沒有演示而已。
同樣的建立風格也用在觸發器身上,想想看我們要設定一個鬧鐘,我們要設定開始執行的時間,執行的頻率,執行的次數,這些一次性搞定,若是你英文還可以,看這些方法應該都明白了。
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group").startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()).build();
哎,不是說好了不按套路出牌的嘛,我要的是每年 5 月的第二個星期日執行呀,可不是定時定頻率啊,別急,後面說。
最後,再建立一個排程器,使用工廠模式建立,這裡選擇 StdSchedulerFactory ,因為這個工廠是可以載入配置檔案的。在框架中會預設給我們提供一套配置,我們也可以在專案根目錄下自行建立。
排程器的建立、繫結任務和觸發器到執行,幾行程式碼搞定。
// 定義一個 Schedule,用於繫結任務和觸發器 SchedulerFactory sf = new StdSchedulerFactory(); Scheduler scheduler = sf.getScheduler(); scheduler.scheduleJob(jobDetail, trigger); scheduler.start();
好了,這裡為此,我們就已經搞定了最牛逼的定時任務的 demo 了。但是,你有沒有發現,若是想實現定時定頻率的執行任務,使用 Timer 就可以啊,為什麼要勞煩大哥出場呢?
這裡提一下 Timer 和 Quartz 的區別吧。
1、Timer 只能執行定時定頻率的任務,而 Quartz 不是。
2、Timer 只有一個執行緒在執行,而 Quartz 有執行緒池,預設開啟 10 個執行緒。
3、Timer 中出現異常,一切 GG,不能記錄事故現場,而 Quartz 可以。
Quartz 想靈活的設定執行時間,依賴誰呢?排程器?NO,那肯定是觸發器啊,其實 Trigger 有兩個實現類,一個是 SimpleTrigger 就是功能比較簡單的那個,預設實現的就是這個,所以上面我們返回就是 SimpleTrigger 的物件。
而另一個重磅級的實現類就是 CronTrigger ,它的觸發機制是基於日曆的,所以,你能想到的每一天,都能給你表示出來,觸發器,重點就在這個時間的表示上面,我們使用 Cron 表示式來表示執行的時間。
舉個例子吧 "* * * * * ? *" 表示每秒鐘都執行,"0 15 10 ? 6L 2018-2020" 表示 2018-2020 年的每個月的最後一個星期五上午 10:15 執行。你會想,WTF ? 這是什麼鬼,我不打算細說這個 Cron 表示式怎麼寫,因為不需要自己寫!簡單說,這就類似與正則表示式,而我們可以用線上生成工具來生成他們。
你只需要搜尋 Cron 表示式線上生成器即可。
有了隨心所欲的時間之後,我們就可以使用 CronTrigger 來定義觸發器了,核心就在 Cron 表示式上面。
CronTrigger cronTrigger = (CronTrigger)TriggerBuilder.newTrigger().withIdentity("trigger1", "group").startNow() .withSchedule(CronScheduleBuilder.cronSchedule("* * * * * ? *")).build();
至於多執行緒和持久化相關的類都在排程器中,不得不說 Quartz 封裝的就是好呀,使用者根本感覺不到這些,在 StdSchedulerFactory 的內部,封裝了核心排程器 QuartzScheduler ,用於持久化任務的 JobStore ,多執行緒相關的執行緒池、執行緒執行器。
但是我這種沒有具體業務需求的人是不會深入研究的,對了,不要忘了 Quartz 的可配置的特性,簡直就是逆天,沒有業務場景應用的我也就想想得了。
以上也就是帶你瞭解一下 Quartz,自己玩玩完全沒問題。但是,你要是還不知道 Timer ,那就是你的問題了,可以回頭看看這篇。