1. 程式人生 > >Quartz使用總結。---在某一個有規律的時間點幹某件事。

Quartz使用總結。---在某一個有規律的時間點幹某件事。

廢話的前言

以前憑藉年輕,凡事都靠腦記。現在工作幾年後發現,很多以前看過、用過的東西,再次拿起的時候總覺得記不牢靠。"好記性不如爛筆頭"應該是某位上了年紀的大叔的切膚之痛(僅次於上了年紀的難言之癮)。

我覺得這事得怪怪中國的應試教育,中國的考試方式就是要求把腦袋當資料庫,以前中學那點知識,確實還能裝得下。但現在所需的知識量再一次性裝入大腦,就是記憶體溢位的節奏。另,再相信什麼人腦只開發5%的蠢話了(「人腦只用了不到 5%」 的說法是否確有科學依據?)。更可行的方式,應該學學資料庫,大腦只記憶知識的索引,而把知識的本身定義在外部的儲存中(比如筆記)。基於這個理念,現在準備學著寫點總結性的筆記。

那為什麼不能基於google學習呢?因為google的索引不是你自己,不能精確找到你想要的東西。但它的好處是更海量,能給你原本壓根不知道東西。所以,配合使用,療效更好。

Quartz可以用來做什麼?

Quartz是一個任務排程框架。比如你遇到這樣的問題

  • 想每月25號,信用卡自動還款
  • 想每年4月1日自己給當年暗戀女神發一封匿名賀卡
  • 想每隔1小時,備份一下自己的愛情動作片 學習筆記到雲盤

這些問題總結起來就是:在某一個有規律的時間點幹某件事。並且時間的觸發的條件可以非常複雜(比如每月最後一個工作日的17:50),複雜到需要一個專門的框架來幹這個事。 Quartz就是來幹這樣的事,你給它一個觸發條件的定義,它負責到了時間點,觸發相應的Job起來幹活。

一個簡單的示例

這裡面的所有例子都是基於Quartz 2.2.1

package com.test.quartz;

import static org.quartz.DateBuilder.newDate;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

import java.util.GregorianCalendar
; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.Trigger; import org.quartz.impl.StdSchedulerFactory; import org.quartz.impl.calendar.AnnualCalendar; public class QuartzTest { public static void main(String[] args) { try { //建立scheduler Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); //定義一個Trigger Trigger trigger = newTrigger().withIdentity("trigger1", "group1") //定義name/group .startNow()//一旦加入scheduler,立即生效 .withSchedule(simpleSchedule() //使用SimpleTrigger .withIntervalInSeconds(1) //每隔一秒執行一次 .repeatForever()) //一直執行,奔騰到老不停歇 .build(); //定義一個JobDetail JobDetail job = newJob(HelloQuartz.class) //定義Job類為HelloQuartz類,這是真正的執行邏輯所在 .withIdentity("job1", "group1") //定義name/group .usingJobData("name", "quartz") //定義屬性 .build(); //加入這個排程 scheduler.scheduleJob(job, trigger); //啟動之 scheduler.start(); //執行一段時間後關閉 Thread.sleep(10000); scheduler.shutdown(true); } catch (Exception e) { e.printStackTrace(); } } }
package com.test.quartz;

import java.util.Date;

import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class HelloQuartz implements Job {
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDetail detail = context.getJobDetail();
        String name = detail.getJobDataMap().getString("name");
        System.out.println("say hello to " + name + " at " + new Date());
    }
}

這個例子很好的覆蓋了Quartz最重要的3個基本要素:

  • Scheduler:排程器。所有的排程都是由它控制。
  • Trigger: 定義觸發的條件。例子中,它的型別是SimpleTrigger,每隔1秒中執行一次(什麼是SimpleTrigger下面會有詳述)。
  • JobDetail & Job: JobDetail 定義的是任務資料,而真正的執行邏輯是在Job中,例子中是HelloQuartz。 為什麼設計成JobDetail + Job,不直接使用Job?這是因為任務是有可能併發執行,如果Scheduler直接使用Job,就會存在對同一個Job例項併發訪問的問題。而JobDetail & Job 方式,sheduler每次執行,都會根據JobDetail建立一個新的Job例項,這樣就可以規避併發訪問的問題。

Quartz API

Quartz的API的風格在2.x以後,採用的是DSL風格(通常意味著fluent interface風格),就是示例中newTrigger()那一段東西。它是通過Builder實現的,就是以下幾個。(** 下面大部分程式碼都要引用這些Builder ** )

//job相關的builder
import static org.quartz.JobBuilder.*;

//trigger相關的builder
import static org.quartz.TriggerBuilder.*;
import static org.quartz.SimpleScheduleBuilder.*;
import static org.quartz.CronScheduleBuilder.*;
import static org.quartz.DailyTimeIntervalScheduleBuilder.*;
import static org.quartz.CalendarIntervalScheduleBuilder.*;

//日期相關的builder
import static org.quartz.DateBuilder.*;

DSL風格寫起來會更加連貫,暢快,而且由於不是使用setter的風格,語義上會更容易理解一些。對比一下:

JobDetail jobDetail=new JobDetailImpl("jobDetail1","group1",HelloQuartz.class);
jobDetail.getJobDataMap().put("name", "quartz");

SimpleTriggerImpl trigger=new SimpleTriggerImpl("trigger1","group1");
trigger.setStartTime(new Date());
trigger.setRepeatInterval(1);
trigger.setRepeatCount(-1);

關於name和group

JobDetail和Trigger都有name和group。

name是它們在這個sheduler裡面的唯一標識。如果我們要更新一個JobDetail定義,只需要設定一個name相同的JobDetail例項即可。

group是一個組織單元,sheduler會提供一些對整組操作的API,比如 scheduler.resumeJobs()。

Trigger

在開始詳解每一種Trigger之前,需要先了解一下Trigger的一些共性。

StartTime & EndTime

startTime和endTime指定的Trigger會被觸發的時間區間。在這個區間之外,Trigger是不會被觸發的。

** 所有Trigger都會包含這兩個屬性 **

優先順序(Priority)

當scheduler比較繁忙的時候,可能在同一個時刻,有多個Trigger被觸發了,但資源不足(比如執行緒池不足)。那麼這個時候比剪刀石頭布更好的方式,就是設定優先順序。優先順序高的先執行。

需要注意的是,優先順序只有在同一時刻執行的Trigger之間才會起作用,如果一個Trigger是9:00,另一個Trigger是9:30。那麼無論後一個優先順序多高,前一個都是先執行。

優先順序的值預設是5,當為負數時使用預設值。最大值似乎沒有指定,但建議遵循Java的標準,使用1-10,不然鬼才知道看到【優先順序為10】是時,上頭還有沒有更大的值。

Misfire(錯失觸發)策略

類似的Scheduler資源不足的時候,或者機器崩潰重啟等,有可能某一些Trigger在應該觸發的時間點沒有被觸發,也就是Miss Fire了。這個時候Trigger需要一個策略來處理這種情況。每種Trigger可選的策略各不相同。

這裡有兩個點需要重點注意:

  • MisFire的觸發是有一個閥值,這個閥值是配置在JobStore的。比RAMJobStore是org.quartz.jobStore.misfireThreshold。只有超過這個閥值,才會算MisFire。小於這個閥值,Quartz是會全部重新觸發。

所有MisFire的策略實際上都是解答兩個問題:

  1. 已經MisFire的任務還要重新觸發嗎?
  2. 如果發生MisFire,要調整現有的排程時間嗎?

比如SimpleTrigger的MisFire策略有:

  • MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY

    這個不是忽略已經錯失的觸發的意思,而是說忽略MisFire策略。它會在資源合適的時候,重新觸發所有的MisFire任務,並且不會影響現有的排程時間。

    比如,SimpleTrigger每15秒執行一次,而中間有5分鐘時間它都MisFire了,一共錯失了20個,5分鐘後,假設資源充足了,並且任務允許併發,它會被一次性觸發。

    這個屬性是所有Trigger都適用。

  • MISFIRE_INSTRUCTION_FIRE_NOW

    忽略已經MisFire的任務,並且立即執行排程。這通常只適用於只執行一次的任務。

  • MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT

    將startTime設定當前時間,立即重新排程任務,包括的MisFire的

  • MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT

    類似MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT,區別在於會忽略已經MisFire的任務

  • MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT

    在下一次排程時間點,重新開始排程任務,包括的MisFire的

  • MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT

    類似於MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT,區別在於會忽略已經MisFire的任務。

  • MISFIRE_INSTRUCTION_SMART_POLICY

    所有的Trigger的MisFire預設值都是這個,大致意思是“把處理邏輯交給聰明的Quartz去決定”。基本策略是,

    1. 如果是隻執行一次的排程,使用MISFIRE_INSTRUCTION_FIRE_NOW
    2. 如果是無限次的排程(repeatCount是無限的),使用MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
    3. 否則,使用MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT

MisFire的東西挺繁雜的,可以參考這篇

Calendar

這裡的Calendar不是jdk的java.util.Calendar,不是為了計算日期的。它的作用是在於補充Trigger的時間。可以排除或加入某一些特定的時間點。

以”每月25日零點自動還卡債“為例,我們想排除掉每年的2月25號零點這個時間點(因為有2.14,所以2月一定會破產)。這個時間,就可以用Calendar來實現。

例子:

AnnualCalendar cal = new AnnualCalendar(); //定義一個每年執行Calendar,精度為天,即不能定義到2.25號下午2:00
java.util.Calendar excludeDay = new GregorianCalendar();
excludeDay.setTime(newDate().inMonthOnDay(2, 25).build());
cal.setDayExcluded(excludeDay, true);  //設定排除2.25這個日期
scheduler.addCalendar("FebCal", cal, false, false); //scheduler加入這個Calendar

//定義一個Trigger
Trigger trigger = newTrigger().withIdentity("trigger1", "group1") 
    .startNow()//一旦加入scheduler,立即生效
    .modifiedByCalendar("FebCal") //使用Calendar !!
    .withSchedule(simpleSchedule()
        .withIntervalInSeconds(1) 
        .repeatForever()) 
    .build();

Quartz體貼地為我們提供以下幾種Calendar,注意,所有的Calendar既可以是排除,也可以是包含,取決於:

  • HolidayCalendar。指定特定的日期,比如20140613。精度到天。
  • DailyCalendar。指定每天的時間段(rangeStartingTime, rangeEndingTime),格式是HH:MM[:SS[:mmm]]。也就是最大精度可以到毫秒。
  • WeeklyCalendar。指定每星期的星期幾,可選值比如為java.util.Calendar.SUNDAY。精度是天。
  • MonthlyCalendar。指定每月的幾號。可選值為1-31。精度是天
  • AnnualCalendar。 指定每年的哪一天。使用方式如上例。精度是天。
  • CronCalendar。指定Cron表示式。精度取決於Cron表示式,也就是最大精度可以到秒。

Trigger實現類

Quartz有以下幾種Trigger實現:

SimpleTrigger

指定從某一個時間開始,以一定的時間間隔(單位是毫秒)執行的任務。

它適合的任務類似於:9:00 開始,每隔1小時,執行一次。

它的屬性有:

  • repeatInterval 重複間隔
  • repeatCount 重複次數。實際執行次數是 repeatCount+1。因為在startTime的時候一定會執行一次。** 下面有關repeatCount 屬性的都是同理。 **

例子:

simpleSchedule()
        .withIntervalInHours(1) 
            
           

相關推薦

Quartz使用總結---在一個規律時間

廢話的前言 以前憑藉年輕,凡事都靠腦記。現在工作幾年後發現,很多以前看過、用過的東西,再次拿起的時候總覺得記不牢靠。"好記性不如爛筆頭"應該是某位上了年紀的大叔的切膚之痛(僅次於上了年紀的難言之癮)。 我覺得這事得怪怪中國的應試教育,中國的考試方式就是要求把腦袋當資料庫,以前中學那點知識,確實還能

git 撤回上一次commit中一個不想添加的文

發現 如果 reset use 查看 不想 一次 文件刪除 git 1. 假設我們修改了文件a,同時修改了IDE的配置文件b 2.此時我們只想添加文件a到commit中,卻不小心將b也添加進去了 3.那麽怎麽撤回呢? 4.第一種方法: 4.1 git reset --

關於搭建私有云的那些z270-ubuntu14

一百塊錢入了一個Z270,改造了一下。首先安裝了ubuntu 14的server版本。出現了無法安裝的現象。這個時候進入shell,掛載U盤的sdv1分割槽,然後把iso檔案掛載為cdrom即可。進入後安裝lamp環境,但是由於環境為php5.59,需要升級為5.6,先解除安

DuerOS大事沒錯,這就是拿AI當年貨的元年

日常 解決 在外 天貓 心情 一半 產品 另一個 結果 話說,再過十幾天就春節了,剛剛忙完年會的你,現在是種什麽心情?開心?馬上要闔家團聚了那是當然的。放松?忙了一年了終於能歇歇,應該的。恐懼?嗯,想一想即將面對的“薛定諤式送禮難題”“巴普洛夫式春節審問”“暴走版串親戚”“

轉行程式設計師?你可能忽略了一

對於資料結構與演算法,我們需要大量的習題實戰才可以更好的吸收理解。有需要的同學同時可以選購“資料結構與演算法365天刷題特訓營”。每週學完基礎知識後帶同學們刷大量與當週內容相關的經典習題。合計約500-800道經典習題讓每一名同學都能快速鞏固消化。(此項為選購,除此之外再無其他選購內容)

一個從小到大排好序的數組現輸入一個數,要求按原來的規律將它插入數組中

cti 分享 bre alt 技術 .cn splice 死循環 set <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title></titl

山科java實驗3-4 假設餐館中每桌顧客點菜記錄的格式為“北京烤鴨:189 西芹百合:15 清蒸鱸魚:80”(每道菜的價格與下一道菜的名字之間有一個空格)編寫一個類的方法,能夠接受鍵盤錄入的符合

假設某餐館中每桌顧客點菜記錄的格式為“北京烤鴨:189 西芹百合:15 清蒸鱸魚:80”(每道菜的價格與下一道菜的名字之間有一個空格)。編寫一個類的方法,能夠接受鍵盤錄入的符合上述格式的點菜內容字串,輸

3.5 編寫一個程式,從標準輸入讀入職員的工作時間(以小時計)和每小時的工資 數,計算並輸出他的工資若職員月工作時間超過 40 小時,則超過部分按原工資的 1.5 倍 來計算

/* 3.5 編寫一個程式,從標準輸入讀入某職員的工作時間(以小時計)和每小時的工資 數,計算並輸出他的工資。若職員月工作時間超過 40 小時,則超過部分按原工資的 1.5 倍 來計算。 */ #include <iostream> using namespac

題目描述 Description 一行N個方格,開始每個格子裡都有一個整數現在動態地提出一些問題和修改:提問的形式是求某一個特定的子區間[a,b]中所有元素的和;修改的規則是指定某一個格子x,加上或

題目描述 Description 一行N個方格,開始每個格子裡都有一個整數。現在動態地提出一些問題和修改:提問的形式是求某一個特定的子區間[a,b]中所有元素的和;修改的規則是指定某一個格子x,加上或者減去一個特定的值A。現在要求你能對每個提問作出正確的回答。1

一個臺階總共有n級,如果一次可以跳1級,也可以跳2級 求總共多少總跳法,並分析演算法的時間複雜度

一個臺階總共有n級,如果一次可以跳1級,也可以跳2級。 求總共有多少總跳法,並分析演算法的時間複雜度。 設:總共有F(n)種跳法 那麼, F(1) = 1; F(2)= 2; F(

素數距離問題 時間限制:3000 ms | 記憶體限制:65535 KB 難度:2 描述 現在給出你一些數,要求你寫出一個程式,輸出這些整數相鄰最近的素數,並輸出其相距長度如果左右等距離長度素數

個人理解:判斷輸入的數是不是輸入只要判斷一個數i從2開始到到i*i小於等於輸入的這個數,對於0,1要直接輸出來0,然後找到左右的兩個素數進行比較大小後打印出即可 #include<stdio.h> #include<string.h>#inclu

未能載入檔案或程式集“****”或它的一個依賴項試圖載入格式不正確的程式解決方案總結

當這個ImageList中沒有影象時編譯也是正常的,但是一旦編譯就會引發這樣的異常。 這個錯誤產生的原因在於,VS2010內部使用的編譯器中,無論是32位還是64位的編譯元件,都是純IL的,也就是在64位系統中是以64位模式執行,這與當前專案使用的平臺設定無關。當編譯的元件引用了一個標記為x86的庫(僅

poj 2828 Buy Tickets(N個人排隊,每一個人都一個val來對應,每一個後來人都會插入當前隊伍的一個位置pos)

Railway tickets were difficult to buy around the Lunar New Year in China, so we must get up early and join a long queue… The Lunar New Year was approachin

一個列印規律圖形的c程式

/* 當n=4 時 輸出: 1 5 2 8 6 3 10 9 7 4 */ #include<stdio.h> main() { int i,j,a[100][100],n,k; printf("enter n:"); scanf("%d",&n

N階,上樓可以一步上一階,也可以一步上二階編寫一個程序,計算共有多少中不同的走法?

技術 告訴 不同的 mis misc 技術分享 blog main print c語言實現,小夥伴們誰要有更好的實現方法,要告訴我呦 #include int main(void) { int f,i,f1=1,f2=2; printf("請輸入樓梯數"); scanf(

未能加載文或程序集“XXXXXX”或它的一個依賴項試圖加載格式不正確的程序

link 依賴項 search 操作系統 true 應用程序 swe clas 格式不正確 原因:操作系統是64位的,但發布的程序引用了一些32位的ddl,所以出現了兼容性的問題解決方案一:如果是64位機器,IIS——應用程序池——高級設置——啟用32位應用程序 :true

未能加載文或程序集“file:///D:/Program Files (x86)/ArcGIS/DeveloperKit10.0/DotNet/ESRI.ArcGIS.3DAnalyst.dll”或它的一個依賴項試圖加載格式不正確的程序 行 129,位置 5

config onf gac mil runt 方案 htm 添加 conf 能加載文件或程序集“file:///C:/Program Files (x86)/ArcGIS/DeveloperKit10.0/DotNet/ESRI.ArcGIS.ADF.Loca

【轉】未能加載文或程序集“XXX”或它的一個依賴項試圖加載格式不正確的程序

平臺 文件 導致 啟用 方法 位置 一個 nbsp cpu “/xxxxx”應用程序中的服務器錯誤。 -------------------------------------------------------------------------------- 未能加載文