任务调度-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.3 Scheduler:调度器,由 StdSchedulerFactory 产生,单例的,可以启动和停止任务,操作trigger和job

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

2.4 Listener:监听器,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

原文地址:https://www.cnblogs.com/tdyang/p/13959637.html