1. 程式人生 > 實用技巧 >任務排程-Quartz

任務排程-Quartz

我們常用的任務排程有:

springTask:spring自帶的,使用@Scheduled註解就可以簡單快速的實現一個任務

Quartz:是一個非常成熟的任務排程工具,可以精確到毫秒級別, 獨立執行,可以整合到容器中, 支援事務(JobStoreCMT ), 支援叢集 ,支援持久化

xxx-job:分散式的任務排程,以前基於Quartz

Elastic-Job:分散式的任務排程,基於Quartz

1、基本結構

依賴:

<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3
.0</version> </dependency>

springboot中

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

2、關鍵角色

2.1 Job:任務物件,任務執行的內容,我們寫的任務需要實現Job介面,context可以獲取到JobDetail中攜帶的資訊

public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("觸發器資訊>>>"+context.getTrigger());
        System.out.println("觸發器名字>>>"+context.getTrigger().getKey().getName());
        System.
out.println("觸發器組名>>>"+context.getTrigger().getKey().getGroup()); System.out.println("任務建立人>>>"+context.getMergedJobDataMap().get("creater")); System.out.println("任務建立時間 >>>"+context.getMergedJobDataMap().get("createTime")); System.out.println("任務建立內容 >>>現在時間"+ LocalDateTime.now()); } }

JobDetail用來包裝job,指定jobName和groupName組成的唯一標識,jobDataMap可以攜帶kv資料,context可以獲取資訊

JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
                //組成唯一標識
                .withIdentity("jobkey","groupkey")
                //JobData攜帶資料,可在context中獲取
                .usingJobData("creater","jack")
                .usingJobData("createTime","2020/11/4")
                .build();

2.2、Trigger:觸發器,有不同的規則來觸發任務的執行。

SimpleTrigger:簡單觸發器, 固定時刻或時間間隔,毫秒

CalendarIntervalTrigger: 基於日曆的觸發器,可以根據月份、年份來觸發,天數和年數不固定也適用,我們可以不需要去計算時間間隔。

DailyTimeIntervalTrigger: 基於日期的觸發器 每天的某個時間段,每週末哪幾天觸發

CronTrigger :基於 Cron 表示式的觸發器,全能 ,線上cron生成器https://qqe2.com/cron

比如這個simpletrigger,每兩秒觸發任務

 ScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(2)
                .repeatForever();

        Trigger trigger = TriggerBuilder.newTrigger()
          //唯一標識 .withIdentity(
"simpletrigger1","simpletrigger1")
          //立刻啟動,可以指定某個時間 .startNow()
          //可以將上面的直接放在這裡面 .withSchedule(simpleScheduleBuilder) .build();

2.3Scheduler:排程器,由 StdSchedulerFactory 產生,單例的,可以啟動和停止任務,操作trigger和job

   SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        scheduler.scheduleJob(jobDetail,trigger);
        scheduler.start();

2.4Listener:監聽器,Quartz 中提供了三種 Listener,監聽 Scheduler 的,監聽 Trigger 的,監聽 Job 的。

我們只需要實現相應的介面,然後在排程器中註冊,既可以實現功能。

JobListener:

public class  implements JobListener {
    @Override
    public String getName() {
        //返回JobListener的名字
        return "JobListenerTest";
    }

    @Override
    public void jobToBeExecuted(JobExecutionContext context) {
        System.out.println("任務呼叫前執行。。。");
    }

    @Override
    public void jobExecutionVetoed(JobExecutionContext context) {
        System.out.println("任務呼叫前,又被TriggerListener否決了執行。。。");
    }

    @Override
    public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
        System.out.println("任務呼叫後執行。。。");
    }
}

排程器中註冊:

SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.scheduleJob(jobDetail,trigger);
//註冊JobListener
scheduler.getListenerManager().addJobListener(new JobListenerTest());
scheduler.start();

執行結果:

TriggerListener:也是在排程器中註冊;

public class TriggerListenerTest implements TriggerListener {
    @Override
    public String getName() {
        //返回監聽器的名稱
        return "TriggerListenerTest";
    }

    @Override
    public void triggerFired(Trigger trigger, JobExecutionContext context) {
        //Trigger 被觸發,Job 上的 execute() 方法將要被執行時,Scheduler 就呼叫這個
        //方法
        System.out.println("Trigger 被觸發");
    }

    @Override
    public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
       /* 在 Trigger 觸發後 , Job 將被執行前時 由 Scheduler呼叫這個方 法 。
        TriggerListener 給了一個選擇去否決Job 的執行。假如這個方法返回true,這
        個 Job 將不會為此次 Trigger 觸發而得到執行,預設為false*/
        return false;
    }

    @Override
    public void triggerMisfired(Trigger trigger) {
        //Trigger 錯過觸發時呼叫
        System.out.println("Trigger 錯過觸發時呼叫");
    }

    @Override
    public void triggerComplete(Trigger trigger, JobExecutionContext context, Trigger.CompletedExecutionInstruction triggerInstructionCode) {
        //Trigger 被觸發並且完成了 Job 的執行時
        System.out.println("Trigger 被觸發並且完成了 Job 的執行");
    }
}

任務執行結果:trigger的triggerFired()JobListenerTest的jobToBeExecuted()呼叫之前執行,

3、Calendar 排除規則

如果要在觸發器的基礎上,排除一些時間區間不執行任務,就要用到 Quartz 的 Calendar 類,可以按年、月、周、日、特定日期、Cron 表示式排除。

AnnualCalendar:排除年中一天或多天

WeeklyCalendar:排除一個星期中的周幾,例如排除週末,預設週六和週日

HolidayCalendar:排除節假日,如國家法定節假日

CronCalendar:排除了由給定的 Cron表示式的時間集合

DailyCalendar:排除某個時間段,比如排除非9:00-17:00的時間

MonthlyCalendar:排除月份中的指定數天,例除排除每月的最後一天或第一天

使用:

public static void main(String[] args) throws SchedulerException, ParseException {
        JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
                .usingJobData("creater","suwu")
                .usingJobData("createTime","2020/11/11")
                .build();

        SimpleScheduleBuilder scheduleBuilder =  SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(2)
                .repeatForever();

      //排除一個星期中的周幾,3表示星期二,1表示星期日 WeeklyCalendar weeklyCalendar
= new WeeklyCalendar(); weeklyCalendar.setDayExcluded(3,true); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("WeeklyCalendar", "WeeklyCalendar") .startNow() // .startAt(date) .modifiedByCalendar("weekly2") .withSchedule(scheduleBuilder) .build(); SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.scheduleJob(jobDetail,trigger); scheduler.addCalendar("weekly2",weeklyCalendar,true,true); scheduler.start(); }

4、任務儲存

Jobstore 用來儲存任務和觸發器相關的資訊,例如所有任務的名稱、數量、狀態等等,可以用來做視覺化介面的管理

Quartz 中有兩種儲存任務的方式,一種在在記憶體,一種是在資料庫,預設是在記憶體中RAMJobstore,存在記憶體中

的壞處就是系統重啟後,儲存在記憶體中的資料都會丟失,所以一般採用持久化處理,存在資料庫JDBCJobStore中

1、首先我們需要配置資料庫的資訊:

org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 使用 quartz.properties,不使用預設配置
org.quartz.jobStore.useProperties:false
#資料庫中 quartz 表的表名字首
org.quartz.jobStore.tablePrefix:QRTZ_
org.quartz.jobStore.dataSource:myQuartz
#配置資料來源
org.quartz.dataSource.myQuartz.driver:com.mysql.jdbc.Driver
org.quartz.dataSource.myQuartz.URL:jdbc:mysql://localhost:3306/boot?useUnicode=true&characterEncoding=utf8
org.quartz.dataSource.myQuartz.user:root
org.quartz.dataSource.myQuartz.password:123456
org.quartz.dataSource.myQuartz.validationQuery=select 0 from dual
org.quartz.threadPool.threadCount: 10
org.quartz.jobStore.misfireThreshold:10

這個配置在quartz.properties的檔案中,自己建立。

quartz會預設載入org.quartz下的quartz.properties檔案,這裡覆蓋了就載入我們自己建立的檔案,檔案需要同名。

預設是RAMJobStore

org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false

org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true

org.quartz.jobStore.misfireThreshold: 60000

org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

2、建立表

quartz已經提供了不同資料庫的建表語句

mysql的建表語句:

#
# In your Quartz properties file, you'll need to set
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#
#
# By: Ron Cordell - roncordell
#  I didn't see this anywhere, so I thought I'd post it here. This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM.

DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;

CREATE TABLE QRTZ_JOB_DETAILS(
SCHED_NAME VARCHAR(120) NOT NULL,
JOB_NAME VARCHAR(190) NOT NULL,
JOB_GROUP VARCHAR(190) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME VARCHAR(250) NOT NULL,
IS_DURABLE VARCHAR(1) NOT NULL,
IS_NONCONCURRENT VARCHAR(1) NOT NULL,
IS_UPDATE_DATA VARCHAR(1) NOT NULL,
REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
JOB_NAME VARCHAR(190) NOT NULL,
JOB_GROUP VARCHAR(190) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
NEXT_FIRE_TIME BIGINT(13) NULL,
PREV_FIRE_TIME BIGINT(13) NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16) NOT NULL,
TRIGGER_TYPE VARCHAR(8) NOT NULL,
START_TIME BIGINT(13) NOT NULL,
END_TIME BIGINT(13) NULL,
CALENDAR_NAME VARCHAR(190) NULL,
MISFIRE_INSTR SMALLINT(2) NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
REPEAT_COUNT BIGINT(7) NOT NULL,
REPEAT_INTERVAL BIGINT(12) NOT NULL,
TIMES_TRIGGERED BIGINT(10) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_CRON_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
CRON_EXPRESSION VARCHAR(120) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(190) NOT NULL,
    TRIGGER_GROUP VARCHAR(190) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_BLOB_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
BLOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_CALENDARS (
SCHED_NAME VARCHAR(120) NOT NULL,
CALENDAR_NAME VARCHAR(190) NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
ENGINE=InnoDB;

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_FIRED_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
ENTRY_ID VARCHAR(95) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
INSTANCE_NAME VARCHAR(190) NOT NULL,
FIRED_TIME BIGINT(13) NOT NULL,
SCHED_TIME BIGINT(13) NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR(16) NOT NULL,
JOB_NAME VARCHAR(190) NULL,
JOB_GROUP VARCHAR(190) NULL,
IS_NONCONCURRENT VARCHAR(1) NULL,
REQUESTS_RECOVERY VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SCHEDULER_STATE (
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_NAME VARCHAR(190) NOT NULL,
LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
CHECKIN_INTERVAL BIGINT(13) NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
ENGINE=InnoDB;

CREATE TABLE QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;

CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);

CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);

CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);

commit;
View Code

3、執行程式,即可在資料庫中查詢到任務的儲存資料

JDBC 的實現方式有兩種,JobStoreSupport 類的兩個子類:

JobStoreTX:在獨立的程式中使用,自己管理事務,不參與外部事務。

JobStoreCMT:如果需要容器管理事 務時,使用它。

當前專案使用的是JobStoreTX

Quartz錯過觸發的處理:https://www.cnblogs.com/daxin/p/3919927.html