Quartz學(xué)習(xí)

Quartz學(xué)習(xí)

(筆記內(nèi)容來源: https://www.cnblogs.com/daxin/archive/2013/05/27/3101972.html)

1.Quartz CronTrigger 最完整配置說明

CronTrigger配置格式:

格式: [秒] [分] [小時] [日] [月] [周] [年]

序號 說明 是否必填 允許填寫的值 允許的通配符
1 0-59 , - * /
2 0-59 , - * /
3 小時 0-23 , - * /
4 1-31 , - * ? / L W
5 1-12 or JAN-DEC , - * /
6 1-7 or SUN-SAT , - * ? / L #
7 empty 或 1970-2099 , - * /

通配符說明:

* 表示所有值. 例如:在分的字段上設(shè)置 "*",表示每一分鐘都會觸發(fā)。

? 表示不指定值。使用的場景為不需要關(guān)心當(dāng)前設(shè)置這個字段的值。例如:要在每月的10號觸發(fā)一個操作,但不關(guān)心是周幾,所以需要周位置的那個字段設(shè)置為"?" 具體設(shè)置為 0 0 0 10 * ?

- 表示區(qū)間。例如 在小時上設(shè)置 "10-12",表示 10,11,12點都會觸發(fā)。

, 表示指定多個值,例如在周字段上設(shè)置 "MON,WED,FRI" 表示周一,周三和周五觸發(fā)

/ 用于遞增觸發(fā)。如在秒上面設(shè)置"5/15" 表示從5秒開始,每增15秒觸發(fā)(5,20,35,50)。 在月字段上設(shè)置'1/3'所示每月1號開始,每隔三天觸發(fā)一次。

L 表示最后的意思。在日字段設(shè)置上,表示當(dāng)月的最后一天(依據(jù)當(dāng)前月份,如果是二月還會依據(jù)是否是潤年[leap]), 在周字段上表示星期六,相當(dāng)于"7"或"SAT"。如果在"L"前加上數(shù)字,則表示該數(shù)據(jù)的最后一個。例如在周字段上設(shè)置"6L"這樣的格式,則表示“本月最后一個星期五"

W 表示離指定日期的最近那個工作日(周一至周五). 例如在日字段上設(shè)置"15W",表示離每月15號最近的那個工作日觸發(fā)。如果15號正好是周六,則找最近的周五(14號)觸發(fā), 如果15號是周未,則找最近的下周一(16號)觸發(fā).如果15號正好在工作日(周一至周五),則就在該天觸發(fā)。如果指定格式為 "1W",它則表示每月1號往后最近的工作日觸發(fā)。如果1號正是周六,則將在3號下周一觸發(fā)。(注,"W"前只能設(shè)置具體的數(shù)字,不允許區(qū)間"-").

# 序號(表示每月的第幾個周幾),例如在周字段上設(shè)置"6#3"表示在每月的第三個周六.注意如果指定"#5",正好第五周沒有周六,則不會觸發(fā)該配置(用在母親節(jié)和父親節(jié)再合適不過了)

Quartz Scheduler 任務(wù)參數(shù)與任務(wù)狀態(tài)

@DisallowConcurrentExecution (簡單來說:不允許任務(wù)還沒結(jié)束,新開線程執(zhí)行任務(wù))

此標(biāo)記用在實現(xiàn)Job的類上面,意思是不允許并發(fā)執(zhí)行,按照我之前的理解是 不允許調(diào)度框架在同一時刻調(diào)用Job類,后來經(jīng)過測試發(fā)現(xiàn)并不是這樣,而是Job(任務(wù))的執(zhí)行時間[比如需要10秒]大于任務(wù)的時間間隔[Interval(5秒)],那么默認(rèn)情況下,調(diào)度框架為了能讓 任務(wù)按照我們預(yù)定的時間間隔執(zhí)行,會馬上啟用新的線程執(zhí)行任務(wù)。否則的話會等待任務(wù)執(zhí)行完畢以后 再重新執(zhí)行!(這樣會導(dǎo)致任務(wù)的執(zhí)行不是按照我們預(yù)先定義的時間間隔執(zhí)行)

測試代碼,這是官方提供的例子。設(shè)定的時間間隔為3秒,但job執(zhí)行時間是5秒,設(shè)置@DisallowConcurrentExecution以后程序會等任務(wù)執(zhí)行完畢以后再去執(zhí)行,否則會在3秒時再啟用新的線程執(zhí)行

org.quartz.threadPool.threadCount = 5 這里配置框架的線程池中線程的數(shù)量,要多配置幾個,否則@DisallowConcurrentExecution不起作用

org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 5
org.quartz.jobStore.class =org.quartz.simpl.RAMJobStore

@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class StatefulDumbJob implements Job {

    /*
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     * 
     * Constants.
     * 
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     */

    public static final String NUM_EXECUTIONS = "NumExecutions";

    public static final String EXECUTION_DELAY = "ExecutionDelay";

    /*
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     * 
     * Constructors.
     * 
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     */

    public StatefulDumbJob() {
    }

    /*
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     * 
     * Interface.
     * 
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     */

    /**
     * <p>
     * Called by the <code>{@link org.quartz.Scheduler}</code> when a <code>{@link org.quartz.Trigger}</code>
     * fires that is associated with the <code>Job</code>.
     * </p>
     * 
     * @throws JobExecutionException
     *           if there is an exception while executing the job.
     */
    public void execute(JobExecutionContext context)
        throws JobExecutionException {
        System.err.println("---" + context.getJobDetail().getKey()
                + " executing.[" + new Date() + "]");

        JobDataMap map = context.getJobDetail().getJobDataMap();

        int executeCount = 0;
        if (map.containsKey(NUM_EXECUTIONS)) {
            executeCount = map.getInt(NUM_EXECUTIONS);
        }

        executeCount++;

        map.put(NUM_EXECUTIONS, executeCount);

        long delay = 5000l;
        if (map.containsKey(EXECUTION_DELAY)) {
            delay = map.getLong(EXECUTION_DELAY);
        }

        try {
            Thread.sleep(delay);
        } catch (Exception ignore) {
        }

        System.err.println("  -" + context.getJobDetail().getKey()
                + " complete (" + executeCount + ").");

    }

}

public class MisfireExample {

    
    public void run() throws Exception {
        Logger log = LoggerFactory.getLogger(MisfireExample.class);

        log.info("------- Initializing -------------------");

        // First we must get a reference to a scheduler
        SchedulerFactory sf = new StdSchedulerFactory();
        Scheduler sched = sf.getScheduler();

        log.info("------- Initialization Complete -----------");

        log.info("------- Scheduling Jobs -----------");

        // jobs can be scheduled before start() has been called

        // get a "nice round" time a few seconds in the future...
        Date startTime = nextGivenSecondDate(null, 15);

        // statefulJob1 will run every three seconds
        // (but it will delay for ten seconds)
        JobDetail job = newJob(StatefulDumbJob.class)
            .withIdentity("statefulJob1", "group1")
            .usingJobData(StatefulDumbJob.EXECUTION_DELAY, 10000L)
            .build();
    
        SimpleTrigger trigger = newTrigger() 
            .withIdentity("trigger1", "group1")
            .startAt(startTime)
            .withSchedule(simpleSchedule()
                    .withIntervalInSeconds(3)
                    .repeatForever())
            .build();
        
        Date ft = sched.scheduleJob(job, trigger);
        log.info(job.getKey() +
                " will run at: " + ft +  
                " and repeat: " + trigger.getRepeatCount() + 
                " times, every " + trigger.getRepeatInterval() / 1000 + " seconds");

        log.info("------- Starting Scheduler ----------------");

        // jobs don't start firing until start() has been called...
        sched.start();

        log.info("------- Started Scheduler -----------------");
        
        try {
            // sleep for ten minutes for triggers to file....
            Thread.sleep(600L * 1000L); 
        } catch (Exception e) {
        }

        log.info("------- Shutting Down ---------------------");

        sched.shutdown(true);

        log.info("------- Shutdown Complete -----------------");

        SchedulerMetaData metaData = sched.getMetaData();
        log.info("Executed " + metaData.getNumberOfJobsExecuted() + " jobs.");
    }



    public static void main(String[] args) throws Exception {

        MisfireExample example = new MisfireExample();
        example.run();
    }

}

@PersistJobDataAfterExecution

此標(biāo)記說明在執(zhí)行完Job的execution方法后保存JobDataMap當(dāng)中固定數(shù)據(jù),在默認(rèn)情況下 也就是沒有設(shè)置 @PersistJobDataAfterExecution的時候 每個job都擁有獨立JobDataMap

否則改任務(wù)在重復(fù)執(zhí)行的時候具有相同的JobDataMap

@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class BadJob1 implements Job {

    public BadJob1() {
    }

    public void execute(JobExecutionContext context)
        throws JobExecutionException {
        JobKey jobKey = context.getJobDetail().getKey();
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        
        int denominator = dataMap.getInt("denominator");
        System.out.println("---" + jobKey + " executing at " + new Date() + " with denominator " + denominator);

        denominator++;
        dataMap.put("denominator", denominator);
    }

}
public class JobExceptionExample {

    public void run() throws Exception {

        // First we must get a reference to a scheduler
        SchedulerFactory sf = new StdSchedulerFactory();
        Scheduler sched = sf.getScheduler();

        // jobs can be scheduled before start() has been called

        // get a "nice round" time a few seconds in the future...
        Date startTime = nextGivenSecondDate(null, 2);

        JobDetail job = newJob(BadJob1.class)
            .withIdentity("badJob1", "group1")
            .usingJobData("denominator", "0")
            .build();
        
        SimpleTrigger trigger = newTrigger() 
            .withIdentity("trigger1", "group1")
            .startAt(startTime)
            .withSchedule(simpleSchedule()
                    .withIntervalInSeconds(2)
                    .repeatForever())
            .build();

        Date ft = sched.scheduleJob(job, trigger);
        
        //任務(wù)每2秒執(zhí)行一次 那么在BadJob1的方法中拿到的JobDataMap的數(shù)據(jù)是共享的.
        //這里要注意一個情況: 就是JobDataMap的數(shù)據(jù)共享只針對一個BadJob1任務(wù)。
        //如果在下面在新增加一個任務(wù) 那么他們之間是不共享的 比如下面
        
        JobDetail job2 = newJob(BadJob1.class)
                .withIdentity("badJob1", "group1")
                .usingJobData("denominator", "0")
                .build();
        
        SimpleTrigger trigger2 = newTrigger() 
                .withIdentity("trigger1", "group1")
                .startAt(startTime)
                .withSchedule(simpleSchedule()
                        .withIntervalInSeconds(2)
                        .repeatForever())
                .build();
        
        //這個job2與job執(zhí)行的JobDataMap不共享
        sched.scheduleJob(job2, trigger2);
        
        sched.start();

        try {
            // sleep for 30 seconds
            Thread.sleep(30L * 1000L);
        } catch (Exception e) {
        }

        sched.shutdown(false);
    }

    public static void main(String[] args) throws Exception {

        JobExceptionExample example = new JobExceptionExample();
        example.run();
    }

}

requestRecovery的意思是當(dāng)任務(wù)在執(zhí)行過程中出現(xiàn)意外 比如服務(wù)器down了 那么在重啟時候是否恢復(fù)任務(wù)

JobDetail job = newJob(HelloJob.class)
    .withIdentity("job1", "group1")
    .storeDurably() 
    .requestRecovery()
    .build();

Quartz Scheduler當(dāng)任務(wù)中出現(xiàn)異常時的處理策略(JobExecutionExceptions)

問題1 如果任務(wù)執(zhí)行發(fā)生錯誤了怎么辦!
Quartz提供了二種解決方法:

  • 1 立即重新執(zhí)行任務(wù)
  • 2 立即停止所有相關(guān)這個任務(wù)的觸發(fā)器

問題2 怎么去執(zhí)行呢?
Quartz的解決方式是:在你的程序出錯時,用Quartz提供的JobExecutionException類相關(guān)方法很好的解決
一、立即重新執(zhí)行該任務(wù)

當(dāng)任務(wù)中出現(xiàn)異常時,我們捕獲它,然后轉(zhuǎn)換為JobExecutionExceptions異常拋出,同時可以控制調(diào)度引擎立即重新執(zhí)行這個任務(wù)。

try {
        int zero = 0;
        int calculation = 4815 / zero;
    } 
    catch (Exception e) {
        _log.info("--- Error in job!");
        JobExecutionException e2 = 
            new JobExecutionException(e);
        // this job will refire immediately
        e2.refireImmediately();
        throw e2;
    }

二、取消所有與這個任務(wù)關(guān)聯(lián)的觸發(fā)器

try {
    int zero = 0;
    int calculation = 4815 / zero;
} 
catch (Exception e) {
    _log.info("--- Error in job!");
    JobExecutionException e2 = 
        new JobExecutionException(e);
    // Quartz will automatically unschedule
    // all triggers associated with this job
    // so that it does not run again
    e2.setUnscheduleAllTriggers(true);
    throw e2;
}

Quartz Scheduler與Spring集成(一) 基礎(chǔ)配置與常見問題

https://www.cnblogs.com/daxin/archive/2013/05/29/3107178.html

常用操作代碼:
http://www.quartz-scheduler.org/documentation/quartz-2.3.0/cookbook/MultipleSchedulers.html

Quartz How-To:Defining a Job (with input data)

Job:

public class PrintPropsJob implements Job {

    public PrintPropsJob() {
        // Instances of Job must have a public no-argument constructor.
    }

    public void execute(JobExecutionContext context)
            throws JobExecutionException {

        JobDataMap data = context.getMergedJobDataMap();
        System.out.println("someProp = " + data.getString("someProp"));
    }

}

Define job instance:

JobDetail job1 = newJob(MyJobClass.class)
    .withIdentity("job1", "group1")     //標(biāo)識任務(wù)
    .usingJobData("someProp", "someValue")      //input data 
    .build();

Quartz How-To: Scheduling a Job

// Define job instance
JobDetail job1 = newJob(ColorJob.class)
    .withIdentity("job1", "group1")
    .build();

// Define a Trigger that will fire "now", and not repeat
Trigger trigger = newTrigger()
    .withIdentity("trigger1", "group1")
    .startNow()
    .build();

// Schedule the job with the trigger
sched.scheduleJob(job, trigger);

How-To: Update an existing job

   
// Add the new job to the scheduler, instructing it to "replace"
//  the existing job with the given name and group (if any)
JobDetail job1 = newJob(MyJobClass.class)
    .withIdentity("job1", "group1")
    .build();

// store, and set overwrite flag to 'true'     
scheduler.addJob(job1, true);

How-To: Updating a trigger

有一些業(yè)務(wù)場景,我們需要手動去更新任務(wù)的觸發(fā)時間,比如某個任務(wù)是每隔10分鐘觸發(fā)一次,現(xiàn)在需要改成每隔20分鐘觸發(fā)一次,這樣既就需要手動的更新觸發(fā)器
官方的例子:
http://www.quartz-scheduler.org/documentation/quartz-2.1.x/cookbook/UpdateTrigger

Replacing a trigger 替換觸發(fā)器,通過triggerkey移除舊的觸發(fā)器,同時添加一個新的進(jìn)去。

// Define a new Trigger 
Trigger trigger = newTrigger()
    .withIdentity("newTrigger", "group1")
    .startNow()
    .build();

// tell the scheduler to remove the old trigger with the given key, and put the new one in its place
sched.rescheduleJob(triggerKey("oldTrigger", "group1"), trigger);

但是有一個地方需要注意:sched.rescheduleJob(triggerKey("oldTrigger", "group1"), trigger); 這個方法返回一個Date.

如果返回 null 說明替換失敗,原因就是舊觸發(fā)器沒有找到,所以新的觸發(fā)器也不會設(shè)置進(jìn)去.

How-To: Using Job Listeners

Quartz Scheduler 可以對Job(任務(wù))建立一個監(jiān)聽器,分別對任務(wù)執(zhí)行 之前-之后-取消 3個狀態(tài)進(jìn)行監(jiān)聽。

實現(xiàn)監(jiān)聽器需要實現(xiàn)JobListener接口,然后注冊到Scheduler上就可以了。

一:首先寫一個監(jiān)聽器實現(xiàn)類

package com.gary.operation.jobdemo.example1;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;

public class MyJobListener implements JobListener {

    @Override
    public String getName() {
        return "MyJobListener";
    }

    /**
     * (1)
     * 任務(wù)執(zhí)行之前執(zhí)行
     * Called by the Scheduler when a JobDetail is about to be executed (an associated Trigger has occurred).
     */
    @Override
    public void jobToBeExecuted(JobExecutionContext context) {
        System.out.println("MyJobListener.jobToBeExecuted()");
    }

    /**
     * (2)
     * 這個方法正常情況下不執(zhí)行,但是如果當(dāng)TriggerListener中的vetoJobExecution方法返回true時,那么執(zhí)行這個方法.
     * 需要注意的是 如果方法(2)執(zhí)行 那么(1),(3)這個倆個方法不會執(zhí)行,因為任務(wù)被終止了嘛.
     * Called by the Scheduler when a JobDetail was about to be executed (an associated Trigger has occurred),
     * but a TriggerListener vetoed it's execution.
     */
    @Override
    public void jobExecutionVetoed(JobExecutionContext context) {
        System.out.println("MyJobListener.jobExecutionVetoed()");
    }

    /**
     * (3)
     * 任務(wù)執(zhí)行完成后執(zhí)行,jobException如果它不為空則說明任務(wù)在執(zhí)行過程中出現(xiàn)了異常
     * Called by the Scheduler after a JobDetail has been executed, and be for the associated Trigger's triggered(xx) method has been called.
     */
    @Override
    public void jobWasExecuted(JobExecutionContext context,
            JobExecutionException jobException) {
        System.out.println("MyJobListener.jobWasExecuted()");
    }

}

二:將這個監(jiān)聽器注冊到Scheduler
假設(shè)有一個任務(wù)的key是 job1與 group1

// define the job and tie it to our HelloJob class
        JobDetail job = newJob(HelloJob.class)
            .withIdentity("job1", "group1")
            .build();
全局注冊,所有Job都會起作用
Registering A JobListener With The Scheduler To Listen To All Jobs
sched.getListenerManager().addJobListener(new MyJobListener());
指定具體的任務(wù)
Registering A JobListener With The Scheduler To Listen To A Specific Job
Matcher<JobKey> matcher = KeyMatcher.keyEquals(new JobKey("job1", "group1"));
sched.getListenerManager().addJobListener(new MyJobListener(), matcher);
指定一組任務(wù)
Registering A JobListener With The Scheduler To Listen To All Jobs In a Group
GroupMatcher<JobKey> matcher = GroupMatcher.jobGroupEquals("group1");
sched.getListenerManager().addJobListener(new MyJobListener(), matcher);
可以根據(jù)組的名字匹配開頭和結(jié)尾或包含
GroupMatcher<JobKey> matcher = GroupMatcher.groupStartsWith("g");
GroupMatcher<JobKey> matcher = GroupMatcher.groupContains("g");
sched.getListenerManager().addJobListener(new MyJobListener(), matcher);

How-To: Trigger That Executes Every Day

Using CronTrigger

 trigger = newTrigger()
    .withIdentity("trigger3", "group1")
    .startNow()
    .withSchedule(dailyAtHourAndMinute(15, 0)) // fire every day at 15:00
    .build();

Using SimpleTrigger

trigger = newTrigger()
    .withIdentity("trigger3", "group1")
    .startAt(tomorrowAt(15, 0, 0)  // first fire time 15:00:00 tomorrow
    .withSchedule(simpleSchedule()
            .withIntervalInHours(24) // interval is actually set at 24 hours' worth of milliseconds
            .repeatForever())
    .build();

Using CalendarIntervalTrigger

trigger = newTrigger()
    .withIdentity("trigger3", "group1")
    .startAt(tomorrowAt(15, 0, 0)  // 15:00:00 tomorrow
    .withSchedule(calendarIntervalSchedule()
            .withIntervalInDays(1)) // interval is set in calendar days
    .build();

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容