簡單Quartz學習筆記


前言

新手向筆記,來源多爲網上博客例子。


SpringBoot整合Quartz簡單完整例子
SpringBoot整合Quartz作爲調度中心完整實用例子
spring-boot-2.0.3之quartz集成,不是你想的那樣哦!
精通SpringBoot——第十篇:使用Quartz實現動態配置定時任務
quartz初次使用踩坑記


Quartz 框架

Quartz任務調度(1)概念例析快速入門

  • 在現實開發中,我們常常會遇到需要系統在特定時刻完成特定任務的需求,通過引介增強來簡單地模擬實現了一個定時器。它可能只需要我們自己維護一條線程就足以實現定時監控。
    但在實際開發中,我們遇到的需求會複雜很多,可能涉及多點任務調度,需要我們多線程併發協作、線程池的維護、對運行時間規則進行更細粒度的規劃、運行線程現場的保持與恢復等等。如果我們選擇自己來造輪子,可能會遇到許多難題。這時候,引入Quartz任務調度框架會是一個很好的選擇,它:
  1. 允許我們靈活而細粒度地設置任務觸發時間,比如常見的每隔多長時間,或每天特定時刻、特定日子(如節假日),都能靈活地配置這些成相應時間點來觸發我們的任務
  2. 提供了調度環境的持久化機制,可以將任務內容保存到數據庫中,在完成後刪除記錄,這樣就能避免因系統故障而任務尚未執行且不再執行的問題。
  3. 提供了組件式的偵聽器、各種插件、線程池等。通過偵聽器,我們可以引入我們的事件機制,配合上異步調用,既能爲我們的業務處理類解耦,同時還可能提升用戶體驗

Quartz API

  • 基本要素:
    Scheduler:任務調度器。所有的調度都是由它控制
    Job:由希望由調度程序執行的組件實現的接口
    JobDetail:用於定義任務的實例,如任務名稱
    Trigger(即觸發器):定義觸發的條件。如,每隔1秒執行一次
  • JobBuilder:用於定義/構建JobDetail實例,用於定義作業的實例
    TriggerBuilder :用於定義/構建觸發器實例

Quartz 數據庫表

注意:cron方式需要用到的4張數據表:
qrtz_job_details;qrtz_triggers;qrtz_cron_triggers;qrtz_fired_triggers

  • QRTZ_JOB_DETAILS
    存儲每一個已配置的任務Job的詳細信息

  • QRTZ_TRIGGERS
    存儲已配置的觸發器Trigger的信息

  • QRTZ_CRON_TRIGGERS
    存儲Cron Trigger,包括Cron表達式和時區信息

  • QRTZ_FIRED_TRIGGERS
    存儲每個正在執行的觸發器Trigger相關的狀態信息,以及相聯Job的執行信息

  • QRTZ_SCHEDULER_STATE
    記錄 調度器(每個機器節點)的生命狀態

  • QRTZ_LOCKS
    記錄程序的悲觀鎖(防止多個節點同時執行同一個定時任務)

  • QRTZ_SIMPLE_TRIGGERS
    存儲已配置的Simple Trigger的信息

  • QRTZ_SIMPROP_TRIGGERS
    存儲CalendarIntervalTrigger和DailyTimeIntervalTrigger兩種類型的觸發器

  • QRTZ_BLOB_TRIGGERS
    Trigger作爲Blob類型存儲(用於Quartz用戶用JDBC創建他們自己定製的Trigger類型,JobStore並不知道如何存儲實例的時候)

  • QRTZ_CALENDARS
    以Blob類型存儲Quartz的Calendar日曆信息,quartz可配置一個日曆來指定一個時間範圍

  • QRTZ_PAUSED_TRIGGER_GRPS
    存儲已暫停的Trigger組的信息


搭建任務調度工程的簡單流程

知乎:最牛逼的任務調度工具 | Quartz

1. 創建 Job 類

描述任務要執行的具體內容,任務要做什麼,比如打印輸出文字等等
注:Job類必須單獨一個文件,必須爲public

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

/**
 * 任務執行的內容
 * 注:必須單獨一個文件
 * 注:必須爲public
 **/
public class MyJob1 implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("...abc打印123456...");
    }
}

2. 創建 JobDetail

任務的信息,如名稱、組別、描述等

// 2. 創建 JobDetail
JobDetail jobDetail =JobBuilder.newJob(MyJob1.class)
                            .withIdentity("job1", "group1")
                            .withDescription("MyJob1Detail des.")
                            .build();

3. 創建觸發器 Trigger

觸發器的信息,如名稱、組別、描述等
觸發器開始執行的時間、週期、間隔等;
TriggerBuilder.newTrigger() 建立;接着定義各種屬性;

SimpleTrigger

普通的觸發器

// 3. 創建觸發器Trigger
Trigger trigger =
       TriggerBuilder.newTrigger()
               .withIdentity("trigger1", "group")
               .withDescription("trigger1 des.")
//               .startNow()
				.startAt(DateBuilder.dateOf(10, 00, 00))
               .endAt(DateBuildr.dateOf(11,00,00))
               .withSchedule(SimpleScheduleBuilder
                       .simpleSchedule()
                       // 間隔
                       .withIntervalInSeconds(1)
                       // 重複次數,7次
//                       .repeatForever()
                       .withRepeatCount(7)
                       // 若失效,之後再恢復並馬上執行
                       .withMisfireHandlingInstructionFireNow()
               )
               .forJob("job1", "group1")
               .build();
  • 使用.forJob(“job1”, “group1”):IDEA會顯示 JobName、JobGruop 灰色的信息提示字樣
    代碼塊

CronTrigger

使用基於日曆的觸發機制的觸發器,cron表達式

 // 使用基於日曆的觸發機制的觸發器 CronTrigger
 String cron1 = "* * * * * ? *";
 CronTrigger cronTrigger = (CronTrigger)TriggerBuilder.newTrigger()
         .withIdentity("cron trigger 1", "group")
         .withDescription("cron trigger 1 des.")
         .startNow()
         .withSchedule(CronScheduleBuilder
                 .cronSchedule(cron1)
         )
         .build();

4. 創建調度器 Scheduler

調度器的創建模式:一般用 工廠模式 SchedulerFactory 創建
調度器所要調度的 任務的信息(jobDetail)、所要調度的 觸發器(trigger)
開始執行任務: scheduler.start();

// 4. 創建調度器 Scheduler
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
            Scheduler scheduler = schedulerFactory.getScheduler();
            scheduler.scheduleJob(jobDetail, trigger);
            scheduler.start();

任務的執行方式:串行 / 並行

Quartz框架(三)—任務的並行/串行執行

防止並行

使用註解方式:註解在 org.quartz 包的內部,註解在Job類上邊
@DisallowConcurrentExecution:禁止任務併發執行
@PersistJobDataAfterExecution:正常執行完任務後,JobDataMap 中的數據應該被改動,以被下一次調用時使用

import lombok.extern.log4j.Log4j2;
import org.quartz.*;

/**
* Job 是 定時任務的具體執行邏輯
* JobDetail 是 定時任務的定義
 * 註解 DisallowConcurrentExecution:禁止任務併發執行
 * 註解 PersistJobDataAfterExecution:正常執行完任務後,JobDataMap 中的數據應該被改動,以被下一次調用時用
* @author EalenXie
* @date 2019/7/10
*/
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
@Log4j2
public class SayHelloJobLogic implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        // 任務具體邏輯
        log.info("任務(共5秒)開始 SayHelloJob.execute , start... ");
        
		// 延時5秒
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        log.info("任務(共5秒)結束 SayHelloJob.execute , end... ");
    }
}

並行

較新版本的quartz,StatefulJob 這個類已經過期,所以有狀態的Job類,指的是沒有並行執行那兩個註解(@DisallowConcurrentExecution、@PersistJobDataAfterExecution)的Job類、


觸發器的 misfire 機制

Quartz框架(四)—misfire處理機制
任務框架quartz的misfire的理解

在Quartz中,當一個持久化的觸發器因爲:

  1. 調度器被關閉; scheduler shuwdown
  2. 線程池沒有可用線程; pool no free threads
  3. 項目重啓; project restart
  4. 任務的串行(併發)執行; jobs excute concurrently / serially
    而錯過激活時間,就會發生激活失敗(misfire)。 miss trigger time. / misfire

CronTrigger 使用的策略

  1. 所有的misfile任務馬上執行
  2. CornTrigger默認策略,合併部分misfire,正常執行下一個週期的任務。
    比如在恢復後,共有兩個misFire,則合併爲一個misFire,於是只執行這一個misFire
  3. 所有的misFire都不管,執行下一個週期的任務。
// 所有的misfile任務馬上執行
public static final int MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1;

// 在Trigger中默認選擇 MISFIRE_INSTRUCTION_FIRE_ONCE_NOW 策略
public static final int MISFIRE_INSTRUCTION_SMART_POLICY = 0;

// CornTrigger默認策略,合併部分misfire,正常執行下一個週期的任務。
// 比如在恢復後,共有兩個misFire,則合併爲一個misFire,於是只執行這一個misFire
public static final int MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1;

// 所有的misFire都不管,執行下一個週期的任務。
public static final int MISFIRE_INSTRUCTION_DO_NOTHING = 2;

對應 CronTrigger 類的內部源代碼:

    public CronScheduleBuilder withMisfireHandlingInstructionIgnoreMisfires() {
        this.misfireInstruction = -1;
        return this;
    }
    public CronScheduleBuilder withMisfireHandlingInstructionFireAndProceed() {
        this.misfireInstruction = 1;
        return this;
    }

    public CronScheduleBuilder withMisfireHandlingInstructionDoNothing() {
        this.misfireInstruction = 2;
        return this;
    }

在觸發器的定義中選擇策略:TriggerBuilder.newTrigger()

TriggerBuilder.newTrigger()
	// trigger 名稱、組名
	.withIdentity(jobKey.getName(), jobKey.getGroup())
	// trigger 描述
	.withDescription(description)
	// 使用 Cron 和對應的調度器 CronScheduleBuilder
	.withSchedule(CronScheduleBuilder
	        .cronSchedule(cronExpression)
	        // 選擇 misFire 策略
	
	        // 1.所有的 misFire 都不管,執行下一個週期的任務。
	        .withMisfireHandlingInstructionDoNothing()
	        
			// 不選擇任何策略的話,則默認爲該策略
	        // 2.CronTrigger 默認策略,合併部分misfire,正常執行下一個週期的任務
//            .withMisfireHandlingInstructionFireAndProceed()
	
	        // 3. 所有的misfire任務馬上執行
//            .withMisfireHandlingInstructionIgnoreMisfires()
	
	    )
	
	.usingJobData(jobDataMap)
	.build();

Trigger 觸發器狀態

Quartz框架(六)— Trigger狀態轉換

正常獲取、觸發任務執行的流程:

一個觸發器只能綁定一個Job,但是一個Job可以有多個觸發器

調度器線程執行的時候,首先從triggers表中獲取狀態爲WAITING,並且將要觸發的Trigger。然後將WAITING狀態更新爲ACQUIRED,表示該觸發器搶佔到了,防止其他調度器(實例)搶佔。然後插入觸發器信息以及實例名到FRIED_TRIGGERS表中,狀態爲ACQUIRED。前面的更新和後面的插入是在一個事務中進行的。

該觸發器搶佔到任務後,等待觸發時間的到來。

執行時間到來後,每觸發一次任務,都會在 FIRED_TRIGGERS 表中創建一條記錄,並且狀態爲 EXECUTING

如果任務允許併發執行,此時TRIGGERS表裏的狀態更新爲 WAITING,PAUSED,COMPLETE(不需要執行)

如果任務不允許併發執行,還會把Triggers表裏的狀態更新爲BLOCK或PAUSED_BLOCK。

  • 注意:Triggers表更新時根據 任務名 和 任務所屬組名 來更新,而不是 觸發器名稱 和 觸發器組名 來更新的。
    這就解決了一個任務有多個觸發器的併發問題;然後觸發器線程會創建一個執行環境來執行任務,以便在任務執行完成後更新觸發器的狀態。任務執行完成後,在一個事務中觸發器狀態更新爲WAITING,刪除FIRED_TRIGGERS表裏對應的記錄。

  • 如何避免多個節點執行同一個任務
    qrtz_trigger 表中有 NEXT_FIRE_TIME 字段(下一次觸發時間)。每個任務在即將執行的時候,獲取qrtz_locks 表中的行級鎖,開啓一個事務(更新 qrtz_trigger 表狀態爲ACQUIRED,並且插入 qrtz_fire_trigger 一條數據,起始狀態爲 ACQUIRED)。


異步通知

Quartz框架(八)— Quartz實現異步通知

一個Job綁定多個觸發器

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.example.execute.SayHelloJobLogic;
import lombok.extern.log4j.Log4j2;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import static org.quartz.TriggerBuilder.newTrigger;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Calendar;

/**
 * 測試類
 *@author 小胖
 *@date 2019/07
 **/
@Log4j2
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestQuartzForMoreJob {

    /**
    * 一個任務Job 綁定多個觸發器
    * @author 小胖
    * @date 2019/07
    */
    @Test
    public void MultiTriggersTest() {

        // 設置執行次數:1次
        SimpleScheduleBuilder simpleScheduleBuilder =
                SimpleScheduleBuilder.repeatSecondlyForTotalCount(1);

        // 定義任務信息:前面列出的佔用時間5秒的打印文字Job
        JobDetail jobDetail = JobBuilder.newJob(SayHelloJobLogic.class)
                .withIdentity("job1", "job_group1")
                .storeDurably()
                .build();
                
		// 定義時間格式變量
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        // 當前時間
        Date date = new Date();
        // 時間操作,當前時間加 5秒
        Date date1 = addSecond(date, 5);
        // 時間操作,當前時間加 15秒
        Date date2 = addSecond(date, 15);

        // 日誌打印
        // 當前時間,      如 2020-02-20 09:00:00
        log.info("獲取到任務的時間:" + simpleDateFormat.format(date));
        // 當前時間 + 5s, 如 2020-02-20 09:00:05
        log.info("第一次通知的時間:" + simpleDateFormat.format(date1));
        // 當前時間 + 15s,如 2020-02-20 09:00:15
        log.info("第二次通知的時間:" + simpleDateFormat.format(date2));

        // 觸發器1:當前時間 + 5s
        SimpleTrigger trigger1 = newTrigger().withIdentity("trigger1", "group1")
                .withSchedule(simpleScheduleBuilder)
                .startAt(date1)
                .forJob(new JobKey("job1", "job_group1"))
                .withDescription("trigger1, + 5s ")
                .build();

        // 觸發器2:當前時間 + 15s
        SimpleTrigger trigger2 = newTrigger().withIdentity("trigger2", "group1")
                .withSchedule(simpleScheduleBuilder)
                .startAt(date2)
                .forJob(new JobKey("job1", "job_group1"))
                .withDescription("trigger2, + 15s ")
                .build();

        try {
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

            // 多觸發器關聯
            scheduler.scheduleJob(jobDetail, trigger1);
            scheduler.scheduleJob(trigger2);
            scheduler.start();

            // 任務完成,再過100s,關閉調度器
            Thread.sleep(100000);
            scheduler.shutdown();

        } catch (SchedulerException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
    * 時間操作,對一個具體的時間增加多少秒
    * @param date 時間
     * @param second 增加的秒數
    * @return java.util.Date
    * @author 小胖
    * @date 2019/07
    */
    public static Date addSecond(Date date, int second) {
        java.util.Calendar calendar = java.util.Calendar.getInstance();
        calendar.setTime(date);
//        calendar.add(Calendar.MINUTE, minute);
        calendar.add(Calendar.SECOND, second);
        return calendar.getTime();
    }
    
}

測試截圖:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述


創建任務的步驟優化

SpringBoot整合Quartz簡單完整例子

/**
* Quartz 裏面 一個最簡單,最基本的定時任務 應該包含以下必要基本屬性
* 注意 : 這裏只是方便演示,故寫此類進行簡單說明, 可針對自身業務對此類進行擴展
* @author EalenXie
* @date 2019/07
*/
@Data
@Builder
public class JobInfo {
    /**
     * 定時任務 的名字和分組名
     */
    @NotNull(message = "定時任務的 名字 和 組名 堅決不爲空")
    private JobKey jobKey;

    /**
     * 定時任務 的描述(可以定時任務本身的描述,也可以是觸發器的)
     */
    private String description;

    /**
     * 定時任務 的執行cron
     */
    @NotEmpty(message = "定時任務的執行cron 不能爲空")
    private String cronExpression;

    /**
     * 定時任務 的元數據
     */
    private Map<?, ?> jobDataMap;

    /**
     * 定時任務 的 具體執行邏輯類
     */
    @NotNull(message = "定時任務的具體執行邏輯類 堅決不能爲空")
    private Class<? extends Job> jobClass;
}
/**
* 創建和啓動 定時任務
* @param jobInfo 定時任務
*/
public void startJob(JobInfo jobInfo) throws SchedulerException {
	// 1.定時任務 的 名字和組名
	JobKey jobKey = jobInfo.getJobKey();
	// 2.定時任務 的 元數據
	JobDataMap jobDataMap = getJobDataMap(jobInfo.getJobDataMap());
	// 3.定時任務 的 描述
	String description = jobInfo.getDescription();
	// 4.定時任務 的 邏輯實現類
	Class<? extends Job> jobClass = jobInfo.getJobClass();
	// 創建 JobDetail 、Trigger
	JobDetail jobDetail = getJobDetail(jobKey, description, jobDataMap, jobClass);
	Trigger trigger = getTrigger(jobKey, description, jobDataMap, cron);
	// 啓動調度器
	scheduler.scheduleJob(jobDetail, trigger);
}

/**
* 獲取定時任務的定義
 * JobDetail是任務的定義, Job是任務的執行邏輯
* @param jobKey 定時任務的名稱 組名
 * @param description 定時任務的 描述
 * @param jobDataMap 定時任務的 元數據
 * @param jobClass 定時任務的 真正執行邏輯定義類
* @return org.quartz.JobDetail
*/
public JobDetail getJobDetail(JobKey jobKey,
                              String description,
                              JobDataMap jobDataMap,
                              Class<? extends Job> jobClass) {
    return JobBuilder.newJob(jobClass)
            .withIdentity(jobKey)
            .withDescription(description)
            .setJobData(jobDataMap)
            .usingJobData(jobDataMap)
            .requestRecovery()
            .storeDurably()
            .build();
}
/**
* 獲取Trigger (Job的觸發器,執行規則)
* @param jobKey 定時任務的名稱 組名
 * @param description 定時任務的 描述
 * @param jobDataMap 定時任務的 元數據
 * @param cronExpression 定時任務的 執行cron表達式
* @return org.quartz.Trigger
*/
public Trigger getTrigger(JobKey jobKey,
                          String description,
                          JobDataMap jobDataMap,
                          String cronExpression) {
    return TriggerBuilder.newTrigger()
            // trigger 名稱、組名
            .withIdentity(jobKey.getName(), jobKey.getGroup())
            // trigger 描述
            .withDescription(description)
            // 
            .withSchedule(CronScheduleBuilder
                    .cronSchedule(cronExpression)
                    // 選擇 misFire 策略:
                    // 1. 所有的 misFire 都不管,執行下一個週期的任務。
                    .withMisfireHandlingInstructionDoNothing()

                    // 2.CronTrigger 默認策略,合併部分misfire,正常執行下一個週期的任務
//                        .withMisfireHandlingInstructionFireAndProceed()

                    // 3. 所有的misfile任務馬上執行
//                        .withMisfireHandlingInstructionIgnoreMisfires()

                )
            .usingJobData(jobDataMap)
            .build();
}
/**
* 獲取任務的元數據
* @param map 不限量的(序列化的)數據對象
* @return org.quartz.JobDataMap
*/
public JobDataMap getJobDataMap(Map<?, ?> map) {
    return map == null ? new JobDataMap() : new JobDataMap(map);
}

監聽器 Listener

用於當任務調度中你所關注事件發生時,能夠及時獲取這一事件的通知。類似於任務執行過程中的郵件、短信類的提醒
對Job(任務)建立一個監聽器,分別對任務執行 《之前, 之後, 取消》 3個階段進行監聽;
實現監聽器需要實現JobListener接口,然後註冊到Scheduler上;

  • 全局監聽器非全局監聽器,二者的區別在於:
    全局監聽器:能夠接收到所有的Job/Trigger的事件通知
    非全局監聽器:只能接收到在其上註冊的Job或Trigger的事件,不在其上註冊的Job或Trigger則不會進行監聽

Quartz框架(十)監聽
Quartz使用(4) - Quartz監聽器Listerner
【Quartz】 JobListener、Triggerlistener、SchedulerListener介紹與使用


JobListener

任務調度過程中,與任務Job相關的事件包括:Job開始要執行的提示;;Job執行完成的提示燈
Quartz任務調度(4) JobListener分版本超詳細解析

// (1) Job Listener
// 監聽器 Job Listener
MyJobListener myJobListener = new MyJobListener();

// 指定任務的 Job Listener
KeyMatcher<JobKey> jobMatcher = KeyMatcher.keyEquals(jobDetail.getKey());
// 指定任務的 Job Listener
KeyMatcher<JobKey> cronJobMatcher = KeyMatcher.keyEquals(nonStateJobDetail.getKey());

// 指定組的 Job Listener
GroupMatcher<JobKey> groupJobMatcher = GroupMatcher.jobGroupEquals("group2");

// 創建並註冊,局部的 Job Listener
//            scheduler.getListenerManager().addJobListener(myJobListener, jobMatcher);
//            scheduler.getListenerManager().addJobListener(myJobListener, cronJobMatcher);

// 創建並註冊,全局的 Job Listener
scheduler.getListenerManager().addJobListener(myJobListener, EverythingMatcher.allJobs());

TriggerListener

任務調度過程中,與觸發器Trigger相關的事件包括:觸發器觸發、觸發器未正常觸發、觸發器完成等。
Quartz任務調度(5) TriggerListener分版本超詳細解析

// (2) Trigger Listener
MyTriggerListener myTriggerListener = new MyTriggerListener("MyTriggerListener");

// 獲取 triggerKey
TriggerKey triggerKey = trigger.getKey();
TriggerKey cronTriggerKey = cronTrigger.getKey();

// 指定任務的 Trigger Listener
KeyMatcher<TriggerKey> triggerMatcher = KeyMatcher.keyEquals(triggerKey);
KeyMatcher<TriggerKey> cronTriggerMatcher = KeyMatcher.keyEquals(cronTriggerKey);

// 一個特定組的 Trigger Listener
//            GroupMatcher<TriggerKey> groupTriggerMatcher = GroupMatcher.groupEquals("group1");
//            GroupMatcher<TriggerKey> groupTriggerMatcher1 = GroupMatcher.groupStartsWith("g");
//            GroupMatcher<TriggerKey> groupTriggerMatcher2 = GroupMatcher.groupEndsWith("2");

// 創建並註冊,局部的 Trigger Listener
//            scheduler.getListenerManager().addTriggerListener(myTriggerListener,triggerMatcher);
//            scheduler.getListenerManager().addTriggerListener(myTriggerListener, cronTriggerMatcher);

// 全局的 Trigger Listener
scheduler.getListenerManager().addTriggerListener(myTriggerListener, EverythingMatcher.allTriggers());

SchedulerListener

SchedulerListener會在Scheduler的生命週期中關鍵事件發生時被調用。
與Scheduler有關的事件包括:增加一個Job/Trigger,刪除一個Job/Trigger,Scheduler發生嚴重錯誤,關閉Scheduler等。
Quartz任務調度(6) SchedulerListener分版本超詳細解析

// (3) Scheduler Listener
MySchedulerListener mySchedulerListener = new MySchedulerListener();
scheduler.getListenerManager().addSchedulerListener(mySchedulerListener);

// 5. 啓動任務
// 啓動調度器
log.info("開啓調度器 scheduler start...");
scheduler.start();

// 20秒
try {
        Thread.sleep(20000);
} catch (InterruptedException e) {
        e.printStackTrace();
}

// 暫停Job
scheduler.pauseJob(jobDetail.getKey());
scheduler.pauseJob(nonStateJobDetail.getKey());

// 綁定新的 CronTrigger
String newCron = "0/15 * * * * ? ";
CronTrigger cronTrigger1 = TriggerBuilder.newTrigger()
        .withIdentity("newCronTrigger", "group2")
        .withDescription("a new cron")
        .startNow()
        .withSchedule(CronScheduleBuilder.cronSchedule(newCron))
        .forJob("nonStatedJob", "group2")
        .build();
TriggerKey triggerKey1 = cronTrigger1.getKey();
scheduler.rescheduleJob(triggerKey1, cronTrigger1);

// 恢復運行Job
scheduler.resumeJob(jobDetail.getKey());

// 20秒
try {
        Thread.sleep(20000);
} catch (InterruptedException e) {
        e.printStackTrace();
}

// 刪除Job
scheduler.deleteJob(jobDetail.getKey());

// 卸載觸發器
scheduler.unscheduleJob(triggerKey);
scheduler.unscheduleJob(cronTriggerKey);

// 6. 停止任務
// 停止調度器
log.info("停止調度器 scheduler shutdown...");
scheduler.shutdown();

Quartz 常見問題

Trigger does not reference given job!

java quartz框架創建定時任務異常: Trigger does not reference given job!
Quartz調度器-觸發器不引用給定的作業:Trigger does not reference given job

  • 檢查 JobDetail 定義的 .withIdentity() 的內容,是否與 Trigger 定義的 .forJob() 的內容是否相同,不同則會報錯。
JobDetail jobDetail =
        JobBuilder.newJob(MyJob1.class)
                .withIdentity("job1", "group1")
                .withDescription("jobDetail des.")
                .build();
                
Trigger trigger =
        TriggerBuilder.newTrigger()
                .withIdentity("SimpleTrigger1", "SimpleGroup")
                .withDescription("SimpleTrigger1 des.")
                .startNow()
                .withSchedule(SimpleScheduleBuilder
                        .simpleSchedule()
                        // 間隔
                        .withIntervalInSeconds(2)
                        // 重複次數
                        .repeatForever()
                        // 若失效,之後再恢復並馬上執行
                        .withMisfireHandlingInstructionFireNow()
                )
                // 檢查此處是否與將要綁定的JobDetail中的Job名稱和組名稱是否相同
                .forJob("job1", "group1")
                .build();
    
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();

// 監聽器
MyJobListener myJobListener = new MyJobListener();

KeyMatcher<JobKey> keyMatcher = KeyMatcher.keyEquals(jobDetail.getKey());
scheduler.getListenerManager().addJobListener(myJobListener, keyMatcher);

// 若報錯(Trigger does not reference given job)一般會指向這裏
scheduler.scheduleJob(jobDetail, trigger);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章