Quartz框架详解分析

news2024/12/28 20:51:44

文章目录

  • 1 Quartz框架
    • 1.1 入门demo
    • 1.2 Job 讲解
      • 1.2.1 Job简介
      • 1.2.2 Job 并发
      • 1.2.3 Job 异常
      • 1.2.4 Job 中断
    • 1.3 Trigger 触发器
      • 1.3.1 SimpleTrigger
      • 1.3.2 CornTrigger
    • 1.4 Listener监听器
    • 1.5 Jdbc store
      • 1.5.1 简介
      • 1.5.2 添加pom依赖
      • 1.5.3 建表SQL
      • 1.5.4 配置文件quartz.properties
      • 1.5.5 Demo实践
    • 1.6 quartz集群
      • 1.6.1 简介
      • 1.6.2 配置文件quartz.properties

1 Quartz框架

本例quartz使用版本为

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

点击了解Quartz框架基本知识

1.1 入门demo

几个概念搞清楚先:
触发器 Trigger: 什么时候工作
任务 Job: 做什么工作
调度器 Scheduler: 搭配 Trigger和Job

Job类

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

import java.text.SimpleDateFormat;
import java.util.Date;
public class MyJob implements Job {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDetail jobDetail = context.getJobDetail();
        String email = jobDetail.getJobDataMap().getString("email");

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        String now = sdf.format(new Date());

        System.out.printf("向邮件 %s,发送了一封定时邮件,时间: %s %n",email,now);
    }
}

调用

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.text.SimpleDateFormat;
import java.util.Date;
public class TestQuartz {

    public static void main(String[] args) throws Exception {
        //创建调度器
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        //创建触发器
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("tri_001","tri_group_001")//定义名称和所属的组
                // 根据为触发器配置的时间表,将触发器启动的时间设置为当前时刻(触发器此时可能会触发,也可能不会触发)
                .startAt()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(2) //每隔两秒
                        .withRepeatCount(2) //总共执行3次(第一次执行不基数)
                   ).build();

        //定义一个job
        JobDetail job = JobBuilder.newJob(MyJob.class) //指定干活的类MailJob
                .withIdentity("job_001","job_group_001")//定义任务名称和分组
                .usingJobData("email","test@test.com")//定义属性
                .build();
        //使用jobDateMap
        job.getJobDataMap().put("email","admin@test.com");
        //调度假如这个job
        Date date = scheduler.scheduleJob(job, trigger);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        System.out.printf("定时器开始时间:%s %n",sdf.format(date));

        //启动
        scheduler.start();
        Thread.sleep(20000);
        scheduler.shutdown(true);
    }
}

1.2 Job 讲解

1.2.1 Job简介

job一般需要实现org.quartz.Job的接口,Job 其实是由 3 个部分组成:

  • JobDetail:用于描述这个Job是做什么的
  • 实现Job的类:具体干活的
  • JobDataMap:给 Job 提供参数用的
    这是除了usingJobData 方式之外,还可以是其他方式,像这样
    job.getJobDataMap().put("email", "test@test.com");

1.2.2 Job 并发

默认的情况下,无论上一次任务是否结束或者完成,只要规定的时间到了,那么下一次就开始。
有时候会做长时间的任务,比如数据库备份,这个时候就希望上一次备份成功结束之后,才开始下一次备份,即便是规定时间到了,也不能开始,因为这样很有可能造成数据库被锁死 (几个线程同时备份数据库,引发无法预计的混乱)。

那么在这种情况下,给数据库备份任务增加一个注解就好了:@DisallowConcurrentExecution

job类

import cn.hutool.core.date.DateUtil;
import lombok.SneakyThrows;
import org.quartz.*;

@DisallowConcurrentExecution
public class DatabaseBackupJob implements Job {

    @SneakyThrows
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDetail detail = context.getJobDetail();
        String database = detail.getJobDataMap().getString("database");
        System.out.printf("给数据库 %s 备份,耗时10s,当前时间 %s %n",database, DateUtil.now());
        Thread.sleep(3000);
    }
}

调用类

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

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

public class TestQuartz {

    public static void main(String[] args) throws Exception{
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        //创建触发器
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("tri_001","tri_group_001")
                // 根据为触发器配置的时间表,将触发器启动的时间设置为当前时刻(触发器此时可能会触发,也可能不会触发)
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(2) //每隔两秒
                        .withRepeatCount(3) //重复
                ).build();

        //定义一个job
        JobDetail job = JobBuilder.newJob(DatabaseBackupJob.class)
                .withIdentity("job_002","job_group_002")
                .usingJobData("database","mysql")
                .build();

        //调度假如这个job
        Date date = scheduler.scheduleJob(job, trigger);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        System.out.printf("数据库定时器开始时间:%s %n",sdf.format(date));

        //启动
        scheduler.start();

        Thread.sleep(20000);
        scheduler.shutdown(true);

    }
}

1.2.3 Job 异常

任务里发生异常是很常见的。 异常处理办法通常是两种:

  • 当异常发生,那么就通知所有管理这个 Job 的调度,停止运行它
  • 当异常发生,修改一下参数,马上重新运行

异常一:

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

public class ExceptionJob1 implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        int i=0;
        try{
            System.out.println(1/i);
        }catch (Exception e){
            System.out.println("发生了异常,取消了这个job对应的所有的调度");
            JobExecutionException je = new JobExecutionException(e);
            je.setUnscheduleAllTriggers(true);
            throw je;
        }

    }
}

异常二:

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

public class ExceptionJob2 implements Job {
    static int i=0;
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        try{
            System.out.println("运算结果:"+1/i);
        }catch (Exception e){
            System.out.println("发生了异常,修改下参数,立即重新执行");
            i=1;
            JobExecutionException je = new JobExecutionException(e);
            je.setRefireImmediately(true);
            throw je;
        }
    }
}

调度类,只是改下job中的异常类一或者二即可

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

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

public class TestQuartz {

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

    private static void exceptionJob1() throws Exception{
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        //创建触发器
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("tri_001","tri_group_001")
              
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(2) //每隔两秒
                        .withRepeatCount(3) //重复
                ).build();

        //定义一个job
        JobDetail job = JobBuilder.newJob(ExceptionJob1.class)
                .withIdentity("exceptionJob1","exception_group")
                .build();

        //调度假如这个job
        Date date = scheduler.scheduleJob(job, trigger);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        System.out.printf("数据库定时器开始时间:%s %n",sdf.format(date));

        //启动
        scheduler.start();

        Thread.sleep(20000);
        scheduler.shutdown(true);
    }

1.2.4 Job 中断

业务上,有时候需要中断任务,那么这个Job 需要实现 InterruptableJob 接口而不是 Job 接口,然后就方便中断了

import org.quartz.InterruptableJob;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.UnableToInterruptJobException;

public class StoppableJob implements InterruptableJob {

    private boolean stop = false;
    @Override
    public void interrupt() throws UnableToInterruptJobException {
        System.out.println("被调度叫停");
        stop=true;
    }

    @Override
    public synchronized void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        while (true){
            if(stop)
                break;
            try {
                System.out.println("每隔一秒,进行一次检测,看看是否停止");
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println("持续工作中。。。。");
        }
    }
}

调度类

public class TestQuartz {

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

    }

    private static void stop() throws Exception{
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        //创建触发器
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("tri_001","tri_group_001")
                // 根据为触发器配置的时间表,将触发器启动的时间设置为当前时刻(触发器此时可能会触发,也可能不会触发)
                .startNow()
                .build();

        //定义一个job
        JobDetail job = JobBuilder.newJob(StoppableJob.class)
                .withIdentity("stop_job","stop_job_group")
                .build();

        //调度假如这个job
        Date date = scheduler.scheduleJob(job, trigger);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        System.out.printf("数据库定时器开始时间:%s %n",sdf.format(date));

        //启动
        scheduler.start();

        Thread.sleep(5000);
        System.out.println("过5秒,调度停止job");
        scheduler.interrupt(job.getKey());



        Thread.sleep(20000);
        scheduler.shutdown(true);
    }
}

1.3 Trigger 触发器

1.3.1 SimpleTrigger

Trigger 就是触发器的意思,用来指定什么时间开始触发,触发多少次,每隔多久触发一次,SimpleTrigger 可以方便的实现一系列的触发机制。

Job类都差不多,重点是调用类差别

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

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

public class TestQuartz {

    public static void main(String[] args) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        System.out.println("当前时间是:"+sdf.format(new Date()));
        //下一个8秒的倍数
        Date startTime = DateBuilder.nextGivenSecondDate(null, 8);
        //10 秒后运行
//        startTime = DateBuilder.futureDate(10, DateBuilder.IntervalUnit.SECOND);
        //累计n次,间隔n秒
        System.out.println("延迟后的时间:"+sdf.format(startTime));

        JobDetail job = JobBuilder.newJob(MailJob.class).withIdentity("mailJob", "mailGroup")
                .build();
        SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
                .withIdentity("trigger_simple","trigger_simple_group")
//                .startNow()
                .startAt(startTime)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withRepeatCount(2)
  //                      .repeatForever() //永远运行下去
                        .withIntervalInSeconds(1))
                .build();
        Date date = scheduler.scheduleJob(job, trigger);
        System.out.println("当前时间是:"+sdf.format(new Date()));
        System.out.printf("%s 这个任务会在 %s 准时开始运行,累计运行 %d 次,间隔时间是 %d 毫秒 %n",
                job.getKey(),sdf.format(date),trigger.getRepeatCount()+1,trigger.getRepeatInterval());

        scheduler.start();
        Thread.sleep(20000);
        scheduler.shutdown(true);
    }
}

1.3.2 CornTrigger

CronLinux下的一个定时器,功能很强大,但是表达式更为复杂
CronTrigger 就是用 Cron 表达式来安排触发时间和次数的。
点击了解Corn表达式

import cn.quartz.simple.MailJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.util.Date;

public class TestQuartz {

    public static void main(String[] args) throws Exception {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

        Date startTime = DateBuilder.nextGivenSecondDate(null, 8);

        JobDetail job = JobBuilder.newJob(MailJob.class).withIdentity("mailJob", "mailGroup").build();

        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?"))
                .build();

        // schedule it to run!
        scheduler.scheduleJob(job, trigger);

        System.out.println("使用的Cron表达式是:"+trigger.getCronExpression());
//            System.out.printf("%s 这个任务会在 %s 准时开始运行,累计运行%d次,间隔时间是%d毫秒%n", job.getKey(), ft.toLocaleString(), trigger.getRepeatCount()+1, trigger.getRepeatInterval());

        scheduler.start();

        //等待20秒,让前面的任务都执行完了之后,再关闭调度器
        Thread.sleep(10000);
        scheduler.shutdown(true);
    }
}

1.4 Listener监听器

Quartz 的监听器有Job监听器Trigger监听器Scheduler监听器,对不同层面进行监控。 实际业务用的较多的是 Job监听器,用于监听器是否执行了,其他的用的相对较少,主要讲解Job的

MailJobListener 实现了 JobListener 接口,就必须实现如图所示的4个方法

方法说明
getName()getName() 方法返回一个字符串用以说明JobListener的名称,对于注册为全局的监听器,getName()主要用于记录日志,对于由特定Job引用的JobListener,注册再JobDetail上的监听器名称必须匹配从监听器上 getName() 方法的返回值
jobToBeExecuted()Scheduler在JobDetail将要被执行时调用这个方法
jobExecutionVetoed()Scheduler在JobDetail即将被执行,但又被TriggerListener否决了时调用这个方法
jobWasExecuted()Scheduler在JobDetail 被执行之后调用这个方法
package cn.quartz.listen;

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

public class MailJobListener implements JobListener {

    @Override
    public String getName() {
        return "listener of mail job";
    }

    @Override
    public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {
        System.out.println("准备执行: \t"+jobExecutionContext.getJobDetail().getKey());
    }

    @Override
    public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
        System.out.println("取消执行: \t"+jobExecutionContext.getJobDetail().getKey());
    }

    @Override
    public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
        System.out.println("执行结束: \t"+jobExecutionContext.getJobDetail().getKey());

    }
}

调度类

package cn.quartz.listen;

import cn.hutool.json.JSONUtil;
import cn.quartz.simple.MailJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.KeyMatcher;

import java.util.Date;

public class TestQuartz {

    public static void main(String[] args) throws Exception {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

        Date startTime = DateBuilder.nextGivenSecondDate(null, 8);

        JobDetail job = JobBuilder.newJob(MailJob.class).withIdentity("mailJob", "mailGroup").build();

        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withRepeatCount(2)
                        .withIntervalInSeconds(1))
                .build();

        //增加监听器
        MailJobListener listener = new MailJobListener();
        KeyMatcher<JobKey> keyMatcher = KeyMatcher.keyEquals(job.getKey());
        System.out.println("获取到的 keymatcher :" + JSONUtil.toJsonStr(keyMatcher));
        scheduler.getListenerManager().addJobListener(listener,keyMatcher);

        // schedule it to run!
        scheduler.scheduleJob(job, trigger);
        scheduler.start();

        //等待20秒,让前面的任务都执行完了之后,再关闭调度器
        Thread.sleep(10000);
        scheduler.shutdown(true);
    }
}

1.5 Jdbc store

1.5.1 简介

默认情况,Quartz 的触发器,调度,任务等信息都是放在内存中的,叫做 RAMJobStore。 好处是快速,坏处是一旦系统重启,那么信息就丢失了,就得全部从头来过。
所以Quartz还提供了另一个方式,可以把这些信息存放在数据库做,叫做 JobStoreTX。 好处是就算系统重启了,目前运行到第几次了这些信息都是存放在数据库中的,那么就可以继续原来的步伐把计划任务无缝地继续做下去。 坏处就是性能上比内存慢一些,毕竟数据库读取总是要慢一些的

1.5.2 添加pom依赖

用的是mysql和C3P0连接池,就需要添加这两个的依赖

 <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
        </dependency>
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>

1.5.3 建表SQL

表名描述
QRTZ_CALENDARS存储Quartz的日历信息
QRTZ_CRON_TRIGGERS存储cron类型的Trigger
包括cron表达式和时区信息
QRTZ_FIRED_TRIGGERS存储与已触发的Trigger相关的状态信息以及相关联的Job的执行信息
QRTZ_PAUSED_TRIGGER_GRPS存储已暂停的Trigger组的信息
QRTZ_SCHEDULER_STATE存储Scheduler相关的状态信息
QRTZ_LOCKS如果程序使用了悲观锁,则存储程序的悲观锁信息
QRTZ_JOB_DETAILS存储每一个已经配置的JobDetail信息
QRTZ_SIMPLE_TRIGGERS存储Simple类型的Trigger,包括重复次数,间隔以及已经触发的次数
QRTZ_BLOG_TRIGGERS存储Blog类型的Trigger
QRTZ_TRIGGERS存储已经配置的Trigger的基本信息
QRTZ_TRIGGER_LISTENERS存储Trigger监听器的信息
QRTZ_JOB_LISTENERS存储Job监听器信息

其文件在quartz-scheduler下的org/quartz/impl/jdbcjobstore/tables_mysql_innodb.sql

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;

1.5.4 配置文件quartz.properties


#调度标识名 集群中每一个实例都必须使用相同的名称 (区分特定的调度器实例)
org.quartz.scheduler.instanceName = MyScheduler
#ID设置为自动获取 每一个必须不同 (所有调度器实例中是唯一的)
org.quartz.scheduler.instanceId=AUTO

#数据保存方式为持久化
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#表的前缀
org.quartz.jobStore.tablePrefix = 12QRTZ_
#数据库别名
org.quartz.jobStore.dataSource = mysqlDatabase
#加入集群 true 为集群 false不是集群
org.quartz.jobStore.isClustered = false

org.quartz.threadPool.threadCount = 3
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool

#设置数据源
org.quartz.dataSource.mysqlDatabase.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.mysqlDatabase.URL = jdbc:mysql://localhost:3306/quartz?characterEncoding=utf-8
org.quartz.dataSource.mysqlDatabase.user =root
org.quartz.dataSource.mysqlDatabase.password = root
org.quartz.dataSource.mysqlDatabase.maxConnections = 5

1.5.5 Demo实践

package cn.quartz.jdbc;

import org.quartz.*;

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

@DisallowConcurrentExecution
public class MailJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDetail detail = context.getJobDetail();
        String email = detail.getJobDataMap().getString("email");

        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        String now = sdf.format(new Date());

        System.out.printf("给邮件地址 %s 发出了一封定时邮件, 当前时间是: %s (%s)%n" ,email, now,context.isRecovering());
    }
}
package cn.quartz.jdbc;

import cn.hutool.json.JSONUtil;
import cn.quartz.listen.MailJobListener;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.KeyMatcher;

import java.util.Date;

public class TestQuartz {

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

       try {
            assginNewJob();
        } catch (ObjectAlreadyExistsException e) {
            System.err.println("任务已经存在");
            resumeJobFromDatabase();
        }
    }
    private static void resumeJobFromDatabase() throws Exception {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.start();
        // 等待200秒,让前面的任务都执行完了之后,再关闭调度器
        Thread.sleep(20000);
        scheduler.shutdown(true);
    }


    private static void assginNewJob() throws SchedulerException, InterruptedException {
        // 创建调度器
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        // 定义一个触发器
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1") // 定义名称和所属的租
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1) // 每隔15秒执行一次
                        .withRepeatCount(2)) // 总共执行11次(第一次执行不基数)
                .build();

        // 定义一个JobDetail
        JobDetail job = JobBuilder.newJob(MailJob.class) // 指定干活的类MailJob
                .withIdentity("mailjob1", "mailgroup") // 定义任务名称和分组
                .usingJobData("email", "admin@10086.com") // 定义属性
                .build();

        // 调度加入这个job
        scheduler.scheduleJob(job, trigger);

        // 启动
        scheduler.start();

        // 等待2秒,让前面的任务都执行完了之后,再关闭调度器
        Thread.sleep(20000);
        scheduler.shutdown(true);
    }
}

1.6 quartz集群

1.6.1 简介

Quartz集群,是指在 基于数据库存储 Quartz调度信息 的基础上, 有多个一模一样的 Quartz 应用在运行。
当某一个Quartz 应用重启或者发生问题的时候, 其他的 Quartz 应用会借助数据库这个桥梁探知到它不行了,从而接手把该进行的 Job 调度工作进行下去。
以这种方式保证任务调度的高可用性,即在发生异常重启等情况下,调度信息依然连贯性地进行下去,就好像 Quartz 应用从来没有中断过似的。
在这里插入图片描述

1.6.2 配置文件quartz.properties


#调度标识名 集群中每一个实例都必须使用相同的名称 (区分特定的调度器实例)
org.quartz.scheduler.instanceName = MyScheduler
#ID设置为自动获取 每一个必须不同 (所有调度器实例中是唯一的)
#要进行集群,多个应用调度id instanceId 必须不一样,这里使用AUTO,就会自动分配不同的ID。 目测是本机机器名称加上时间戳
org.quartz.scheduler.instanceId=AUTO

#数据保存方式为持久化
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#表的前缀
org.quartz.jobStore.tablePrefix = 12QRTZ_
#数据库别名
org.quartz.jobStore.dataSource = mysqlDatabase
#加入集群 true 为集群 false不是集群
org.quartz.jobStore.isClustered = true
#每个一秒钟去数据库检查一下,以在其他应用挂掉之后及时补上
org.quartz.jobStore.clusterCheckinInterval = 1000

org.quartz.threadPool.threadCount = 3
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool

#设置数据源
org.quartz.dataSource.mysqlDatabase.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.mysqlDatabase.URL = jdbc:mysql://localhost:3306/quartz?characterEncoding=utf-8
org.quartz.dataSource.mysqlDatabase.user =root
org.quartz.dataSource.mysqlDatabase.password = root
org.quartz.dataSource.mysqlDatabase.maxConnections = 5

代码部分同jdbc store部分

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/440374.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

23-HTTP协议

目录 1.HTTP是什么&#xff1f; 2.HTTP工作过程 3.HTTP协议格式 3.1.抓包工具使用 eg&#xff1a;抓取"必应"的包 PS&#xff1a;HTTP不同版本号之间的区别 3.2.抓包工具原理 3.3.抓包结果分析 ①HTTP 请求&#xff1a; ②HTTP 响应&#xff1a; 3.4.协议…

ArduPilot Kakute F7 AIO DIYF450 without GPS配置

ArduPilot Kakute F7 AIO DIYF450 without GPS配置 1. 源由2. 配置2.1 Kakute F7 AIO相关配置2.1.1 串口规划2.1.2 电传配置2.1.3 GPS配置2.1.4 CRSF接收机配置2.1.5 Compass配置2.1.6 电机配置2.1.7 TX12 遥控器配置 3. 实测效果4. 参考资料 1. 源由 鉴于GPS模块信号质量未达…

3DEXPERIENCE云可以为PLM带来什么?

在消费者领域&#xff0c;云的优势已显而易见&#xff0c;用一个词就可以概括&#xff1a;便利&#xff0c;3DEXPERIENCE云存储服务的用户可以从任何位置在任何设备上访问其数据&#xff0c;只要能够连接到互联网就行了。在一台设备 上所做的更改会立即反映在另一台设备上。 同…

提升10倍写作效率,这5个写作工具,文笔不好的人别错过

记得刚出来上班的时候&#xff0c;我的写作效率很低&#xff0c;经常没有思路&#xff0c;也找不到选题。甚至一两个小时过去了&#xff0c;仍然不知道如何动笔&#xff0c;经常写了删&#xff0c;删了又写。工欲善其事&#xff0c;必先利其器。在写作过程中&#xff0c;需要一…

【数据分析之道-NumPy(五)】numpy迭代数组

文章目录 专栏导读1、前言2、使用python循环语句3、使用nditer函数3.1迭代一维数组3.2迭代二维数组3.3迭代指定顺序的数组3.4迭代时修改数组中的元素 4、使用flat属性5、使用ndenumerate函数6、使用布尔索引总结 专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN Python…

IO多路复用—多线程网络并发通信 select poll epoll

1.IO 多路转接 (复用) ​ IO 多路转接也称为 IO 多路复用&#xff0c;它是一种网络通信的手段&#xff08;机制&#xff09;&#xff0c;通过这种方式可以同时监测多个文件描述符并且这个过程是阻塞的&#xff0c;一旦检测到有文件描述符就绪&#xff08; 可以读数据或者可以写…

Nginx企业级使用1(运维笔记)

Nginx企业级使用1&#xff08;运维笔记&#xff09; 重装和升级 信号参数 Kill 选项参数 pid ##关闭nginx ##快速关闭 kill -INT pid ##优雅关闭 kill -QUIT pid##############实操############## [rootserver01 ~]# ps -ef|grep nginx root 1668 1 0 11:09 ?…

Flask入门和视图--01

1. 概述 虚拟环境搭建和使用 Flask框架的特点&#xff0c;Flask框架的组成 Flask框架中MVT模式开发 蓝图Blueprint的使用 路由Route的使用 请求Request和响应Response的使用 2. Flask简介 2.1 简介 Python后端的2个主流框架:Flask 轻量级框架Django 重型框架Flask是一…

开心档之C++ 信号处理

C 信号处理 目录 C 信号处理 signal() 函数 实例 raise() 函数 实例 信号是由操作系统传给进程的中断&#xff0c;会提早终止一个程序。在 UNIX、LINUX、Mac OS X 或 Windows 系统上&#xff0c;可以通过按 CtrlC 产生中断。 有些信号不能被程序捕获&#xff0c;但是下表…

安全狗入选2023年福建省数字经济核心产业领域创新企业名单

近日&#xff0c;福建省数字福建建设领导小组办公室公布了入选2023年全省数字经济核心产业领域创新企业名单。 作为国内云原生安全领导厂商&#xff0c;安全狗凭借综合表现与优势入选名单&#xff0c;荣膺“未来独角兽”称号。 据悉&#xff0c;此次对“未来独角兽”的评选条件…

调频电视发射机工作原理

我们平常所接触到的电视信号无线传输器材&#xff0c;较多采用调幅方式。原因是调幅方式在整个电视技术领域用得比较普遍&#xff0c;如我们生活中不可或缺的无线和有线电视广播&#xff0c;几乎全部都采用调幅方式。其实&#xff0c;若是用调频方式来传输电视信号&#xff0c;…

Qt Quick - 分隔器综述

Qt Quick - 分隔器综述 一、概述二、MenuSeparator 控件1. 用法&#xff1a; 三、ToolSeparator 控件1. 用法 一、概述 Qt Quick Controls 提供了多种分隔符&#xff0c;其实就是分割一下MenuBar和ToolBar里面的内容。 控件功能MenuSeparator将菜单中的一组项目与相邻项目分开…

Spring Boot + Spring Security基础入门教程

Spring Security简介 Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。Spring Security 致力于为 Java 应用程序提供身份验证和授权的能力。 Spring Security 两大重要核心功能&#xff1a;用户认证&#xff08;Authentication&#xff09;和用户授权&am…

pandas 使用loc和iloc读取行数据或列数据

文章目录 一、 使用loc方法读取数据1.1 读取某行某列的值1.2 读取某个区域1.3 按照条件筛选 二. 使用iloc方法读取数据2.1 读取某行某列的值2.2 读取某个区域的数据 创建一个DataFrame data {name:[张三, 李四, 王五, 赵六],age:[20, 21, 22, 23], gender: [0, 1, 1, 1], stat…

网络工程项目报价单应该怎么写?记住这6个步骤准没错!

作为一名网络工程师&#xff0c;你在向潜在客户提供服务时&#xff0c;编写一个清晰明了的项目报价单是至关重要的。一个好的报价单不仅能够让客户更好地了解你的服务内容&#xff0c;还可以为你的项目提供更高的转化率。在本文中&#xff0c;我们将探讨如何编写一个有效的网络…

一图看懂 xlwt 模块:读写 Excel 文件的数据和格式信息, 资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 xlwt 模块&#xff1a;读写 Excel 文件的数据和格式信息, 资料整理笔记&#xff08;大全&#xff09; 摘要模块图类关系图模块全展开【xlwt】统计常量模块1 xlwt.compat2 x…

Linux系统之部署Linux管理面板1Panel

Linux系统之部署Linux管理面板1Panel 一、1Panel介绍1.1Panel简介2.1Panel特点 二、本地环境规划1.本此实践目的2.本地环境规划 三、检查本地环境1.检查操作系统版本2.检查系统内核版本 四、部署1Panel1.创建安装目录2.一键部署1Panel3.检查1Panel服务运行状态4.检查1Panel监听…

数据结构——二叉搜索树、平衡二叉树、红黑树

数据结构——二叉搜索树 一、二叉搜索树1.二叉搜索树的特性2.二叉搜索树的查找、插入和删除 二、平衡二叉树1.基本介绍2.AVL树的自平衡1&#xff09;自平衡的调整操作2&#xff09;自平衡调整的局面 3.AVL树的代码实现4.AVL树的特点 三、红黑树1.基本介绍2.红黑树的自平衡1&…

秒杀系统如何设计

思路&#xff1a;对于秒杀系统&#xff0c;两个架构优化思路&#xff1a; 1&#xff09;尽量将请求拦截在系统上游 2&#xff09;读多写少的常用多使用缓存 1、限制用户在x秒之内只能提交一次请求 2、同一个uid&#xff0c;或同一类查询&#xff08;例如车次&#xff09;。限制…

配电网光伏/储能双层优化配置模型(选址定容)

目录 1 主要内容 上层目标函数考虑光伏和储能的投资成本。 程序采用模块化编程&#xff0c;并有每个模块功能介绍&#xff0c;方便学习。 2 部分代码 3 程序结果 4 程序结果 1 主要内容 该程序主要方法复现《含高比例可再生能源配电网灵活资源双层优化配置》运行-规划联合…