当前位置:   article > 正文

Quartz与SpringBoot 搞定任务调度_quartz springboot

quartz springboot

一、Quartz简介

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。Quartz的最新版本为Quartz 2.3.2

1、组件简介

Job

  • Job是一个任务接口,开发者定义自己的任务须实现该接口,并重写execute(JobExecutionContext context)方法.
  • Job中的任务有可能并发执行,例如任务的执行时间过长,而每次触发的时间间隔太短,则会导致任务会被并发执行。
  • 为了避免出现上面的问题,可以在Job实现类上使用@DisallowConcurrentExecution,保证上一个任务执行完后,再去执行下一个任务
    JobDetail
  • JobDetail是任务详情。 包含有:任务名称,任务组名称,任务描述、具体任务Job的实现类、参数配置等等信息
  • 可以说JobDetail是任务的定义,而Job是任务的执行逻辑。
    Trigger
    Trigger是一个触发器,定义Job执行的时间规则。
  • 主要触发器:SimpleTrigger,CronTrigger,CalendarIntervalTrigger,DailyTimeIntervalTrigger。SimpleTrigger:从某一个时间开始,以一定的时间间隔来执行任务,重复多少次。
  • CronTrigger: 适合于复杂的任务,使用cron表达式来定义执行规则。
  • CalendarIntervalTrigger:指定从某一个时间开始,以一定的时间间隔执行的任务,时间间隔比SimpleTrigger丰富
  • DailyTimeIntervalTrigger:指定每天的某个时间段内,以一定的时间间隔执行任务。并且它可以支持指定星期。
  • 所有的Trigger都包含了StartTime和endTime这两个属性,用来指定Trigger被触发的时间区间。
  • 所有的Trigger都可以设置MisFire策略.
  • MisFire策略是对于由于系统奔溃或者任务时间过长等原因导致Trigger在应该触发的时间点没有触发.
  • 并且超过了misfireThreshold设置的时间(默认是一分钟,没有超过就立即执行)就算misfire(失火)了。
  • 这个时候就该设置如何应对这种变化了。激活失败指令(Misfire Instructions)是触发器的一个重要属性
    发生Misfire 对于SimpleTrigger的处理策略
//将任务马上执行一次。对于不会重复执行的任务,这是默认的处理策略。
MISFIRE_INSTRUCTION_FIRE_NOW = 1;

// 调度引擎重新调度该任务,立即执行任务,repeat count 保持不变,
// 按照原有制定的执行方案执行repeat count次,但是,如果当前时间,已经晚于end-time,那么这个触发器将不会再被触发
// 简单的说就是,错过了应该触发的时间没有按时执行,但是最终它还是以原来的重复次数执行,就是会比预计终止的时间晚。
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT

//和上面的类似,区别就是不会立马执行,而是在下一个激活点执行,且超时期内错过的执行机会作废。        
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT

// 这个也是重新调度任务,但是它只按照剩余次数来触发,
// 比如,应该执行10次,但是中间错过了3次没有执行,那它最终只会执行剩余次数 7次。
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT

//在下一个激活点执行,并重复到指定的次数。
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT

// 立即执行任务,repeat count 保持不变,就算到了endtime,也继续执行
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

发生Misfire 对于其他的Trigger

//立刻执行一次,然后就按照正常的计划执行。
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW

//目前不执行,然后就按照正常的计划执行。这意味着如果下次执行时间超过了end time,实际上就没有执行机会了。
MISFIRE_INSTRUCTION_DO_NOTHING
  • 1
  • 2
  • 3
  • 4
  • 5

Scheduler

  • 调度器,主要是用来管理Trigger、JobDetail的。
  • Scheduler可以通过组名或者名称来对Trigger和JobDetail来进行管理
  • 一个Trigger只能对应一个Job,但是一个Job可以对应多个Trigger.
  • Scheduler 有两个实现类:RemoteScheduler、StdScheduler。但它是由 SchdulerFactory创建的。
  • SchdulerFactory是个接口,它有两个实现类:StdSchedulerFactory、DirectSchedulerFactory

2、相关Builder介绍

Quartz提供了相应的Builder方便我们进行构造。
JobBuilder
这个主要方便我们构建任务详情,常用方法

  • withIdentity(String name, String group):配置Job名称与组名
  • withDescription(String jobDescription): 任务描述
  • requestRecovery(): 出现故障是否重新执行,默认false
  • storeDurably(): 作业完成后是否保留存储,默认false
  • usingJobData(String dataKey, String value): 配置单个参数key
  • usingJobData(JobDataMap newJobDataMap): 配置多个参数,放入一个map
  • setJobData(JobDataMap newJobDataMap):
    和上面类似,但是这个参数直接指向newJobDataMap,直接设置的参数无效
    TriggerBuilder
    这个主要方便我们构建触发器,常用方法:
  • withIdentity(String name, String group): 配置Trigger名称与组名
  • withIdentity(TriggerKey triggerKey): 配置Trigger名称与组名
  • withDescription(String triggerDescription): 描述
  • withPriority(int triggerPriority): 设置优先级,默认是:5
  • startAt(Date triggerStartTime): 设置开始时间
  • startNow(): 触发器立即生效
  • endAt(Date triggerEndTime): 设置结束时间
  • withSchedule(ScheduleBuilder schedBuilder):
    设置调度builder,下面的builder就是

SimpleScheduleBuilder
几种触发器类型之一,最简单常用的。常用方法:

  • repeatForever():指定触发器将无限期重复
  • withRepeatCount(int
    triggerRepeatCount):指定重复次数,总触发的次数=triggerRepeatCount+1
  • repeatSecondlyForever(int seconds):每隔seconds秒无限期重复
  • repeatMinutelyForever(int minutes):每隔minutes分钟无限期重复
  • repeatHourlyForever(int hours):每隔hours小时无限期重复
  • repeatSecondlyForever():每隔1秒无限期重复
  • repeatMinutelyForever():每隔1分钟无限期重复
  • repeatHourlyForever():每隔1小时无限期重复
  • withIntervalInSeconds(int intervalInSeconds):每隔intervalInSeconds秒执行
  • withIntervalInMinutes(int intervalInMinutes):每隔intervalInMinutes分钟执行
  • withIntervalInHours(int intervalInHours):每隔intervalInHours小时执行
  • withMisfireHandlingInstructionFireNow():失火后的策略为:MISFIRE_INSTRUCTION_FIRE_NOW
    CronScheduleBuilder
    算是非常常用的了,crontab 表达式,常用方法:
  • cronSchedule(String cronExpression):使用cron表达式。
    CalendarIntervalScheduleBuilder
    常用方法:
  • inTimeZone(TimeZone timezone):设置时区
  • withInterval(int timeInterval, IntervalUnit
    unit):相隔多少时间执行,单位有:毫秒、秒、分、时、天、周、月、年
  • withIntervalInSeconds(int intervalInSeconds):相隔秒
  • withIntervalInWeeks(int intervalInWeeks):相隔周
  • withIntervalInMonths(int intervalInMonths):相隔月
    等等方法
    DailyTimeIntervalScheduleBuilder
  • withInterval(int timeInterval, IntervalUnit
    unit):相隔多少时间执行,单位有:秒、分、时,其他单位的不支持会报错
  • withIntervalInSeconds(int intervalInSeconds):相隔秒
  • withIntervalInMinutes(int intervalInMinutes):相隔分
  • withIntervalInHours(int intervalInHours):相隔时
  • onDaysOfTheWeek(Set
    onDaysOfWeek):将触发器设置为在一周的指定日期触发。取值范围可以是1-7,1是星期天,2是星期一…
  • onDaysOfTheWeek(Integer … onDaysOfWeek):和上面一样,3是星期二…7是星期六
  • onMondayThroughFriday():每星期的周一导周五触发
  • onSaturdayAndSunday():每星期的周六周日触发
  • onEveryDay():每天触发
  • withRepeatCount(int repeatCount):重复次数,总的重复次数=1 (at start time) +
    repeatCount
  • startingDailyAt(TimeOfDay timeOfDay):触发的开始时间
  • endingDailyAt(TimeOfDay timeOfDay):触发的结束时间

3、基本使用

引用依赖

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.2</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

在pom.xml文件中,引入

简单例子:

public class Demo {
    public static final String COUNT = "count";

    //这个属性如不是static,那么每次都要实例这个任务类,始终打印为: 1
    private static int num = 1;

    public static void main(String[] args) throws SchedulerException {
        SchedulerFactory schedulerfactory = new StdSchedulerFactory();
//        Scheduler scheduler = schedulerfactory.getScheduler();
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.start();
        JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("jobName", "jobGroupName")
                .usingJobData(COUNT,num).build();
        // 各种builder 的基本使用
        SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(3);
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("*/3 * * * * ?");
        CalendarIntervalScheduleBuilder calendarIntervalScheduleBuilder = CalendarIntervalScheduleBuilder.calendarIntervalSchedule().withInterval(3, DateBuilder.IntervalUnit.SECOND);
        DailyTimeIntervalScheduleBuilder dailyTimeIntervalScheduleBuilder = DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule().withIntervalInSeconds(3);

        Trigger trigger =  TriggerBuilder.newTrigger().withIdentity("jobName", "jobGroupName")
                .withSchedule(dailyTimeIntervalScheduleBuilder)
                .startNow().build();
        scheduler.scheduleJob(job, trigger);
    }

    // 保存在JobDataMap传递的参数
    @PersistJobDataAfterExecution
    @DisallowConcurrentExecution
    public static class HelloJob implements Job{
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
            int count = jobDataMap.getInt(COUNT);
            System.out.println("=========="+ LocalDateTimeUtils.formatDateTimeNow()+",count="+count);
            jobDataMap.put(COUNT,++num);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

是不是感觉很简单,自己定义一个类,然后实现Job接口,重写execute(JobExecutionContext context) 方法,然后使用调度器去调度一下即可。 简单玩完了,和springboot整合一波。

二、与Springboot整合

这个才是重点,Springboot基本是一个Java程序猿必备的技能了。什么框架都得和它整一下。

1、引入依赖

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4

spring官方自己都帮我们搞好了一些配置。

2、配置application.yml

如下:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/quartz?serverTimezone=GMT&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      initialSize: 5
      minIdle: 5
      maxActive: 30
      max-wait: 6000
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      #validation-query: SELECT 1 FROM DUAL
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        #login-username: admin
        #login-password: admin
      filter:
        stat:
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: false
        wall:
          config:
            multi-statement-allow: true
  quartz:
    job-store-type: jdbc #数据库方式
 #是否等待任务执行完毕后,容器才会关闭
    wait-for-jobs-to-complete-on-shutdown=false
    #配置的job是否覆盖已经存在的JOB信息
    overwrite-existing-jobs: false
    jdbc:
      initialize-schema: ALWAYS #不初始化表结构
    properties:
      org:
        quartz:
          scheduler:
            instanceId: AUTO #默认主机名和时间戳生成实例ID,可以是任何字符串,但对于所有调度程序来说,必须是唯一的 对应qrtz_scheduler_state INSTANCE_NAME字段
            #instanceName: clusteredScheduler #quartzScheduler
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX #持久化配置
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate #我们仅为数据库制作了特定于数据库的代理
            useProperties: false #以指示JDBCJobStore将JobDataMaps中的所有值都作为字符串,因此可以作为名称 - 值对存储而不是在BLOB列中以其序列化形式存储更多复杂的对象。从长远来看,这是更安全的,因为您避免了将非String类序列化为BLOB的类版本问题。
            tablePrefix: qrtz_  #数据库表前缀
            misfireThreshold: 60000 #在被认为“失火”之前,调度程序将“容忍”一个Triggers将其下一个启动时间通过的毫秒数。默认值(如果您在配置中未输入此属性)为60000(60秒)。
            clusterCheckinInterval: 5000 #设置此实例“检入”*与群集的其他实例的频率(以毫秒为单位)。影响检测失败实例的速度。
            isClustered: true #打开群集功能
          threadPool: #连接池
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 10
            threadPriority: 5
            threadsInheritContextClassLoaderOfInitializingThread: true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 这里打算搞个集群版的,就算你部署多台服务器,定时任务会自己负载均衡,不会每台服务器都执行。
  • 表的生成,其实可以修改配置,启动的时候自己在数据库生成表。操作方法:
  • 修改:spring.quartz.jdbc.initialize-schema: ALWAYS、说明一下
  • 这里有3个值可选:ALWAYS(每次都生成)、EMBEDDED(仅初始化嵌入式数据源)、NEVER(不初始化数据源)。
  • 表生成之后,再改为never即可。注意一点就是我测试了下,发现只有使用druid数据库连接池才会自动生成表。

3、表的说明

会自动生成的表如下:

//以Blob 类型存储的触发器。 
qrtz_blob_triggers

//存放日历信息, quartz可配置一个日历来指定一个时间范围。 
qrtz_calendars

//存放cron类型的触发器。 
qrtz_cron_triggers
//存储已经触发的trigger相关信息,trigger随着时间的推移状态发生变化,直到最后trigger执行完成,从表中被删除。 
qrtz_fired_triggers 

//存放一个jobDetail信息。 
qrtz_job_details

//job**监听器**。 
qrtz_job_listeners

//Quartz提供的锁表,为多个节点调度提供分布式锁,实现分布式调度,默认有2个锁
qrtz_locks

//存放暂停掉的触发器。
qrtz_paused_trigger_graps

//存储所有节点的scheduler,会定期检查scheduler是否失效
qrtz_scheduler_state

//存储SimpleTrigger 
qrtz_simple_triggers

//触发器监听器。 
qrtz_trigger_listeners

//触发器的基本信息。
qrtz_triggers

//存储CalendarIntervalTrigger和DailyTimeIntervalTrigger两种类型的触发器
qrtz_simprop_triggers
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

重要表字段解析

CREATE TABLE `qrtz_job_details` (
  `SCHED_NAME` varchar(120) COLLATE utf8_bin NOT NULL COMMENT '调度器名,集群环境中使用,必须使用同一个名称——集群环境下”逻辑”相同的scheduler,默认为QuartzScheduler',
  `JOB_NAME` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '集群中job的名字',
  `JOB_GROUP` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '集群中job的所属组的名字',
  `DESCRIPTION` varchar(250) COLLATE utf8_bin DEFAULT NULL COMMENT '描述',
  `JOB_CLASS_NAME` varchar(250) COLLATE utf8_bin NOT NULL COMMENT '集群中个note job实现类的完全包名,quartz就是根据这个路径到classpath找到该job类',
  `IS_DURABLE` varchar(1) COLLATE utf8_bin NOT NULL COMMENT '是否持久化,把该属性设置为1,quartz会把job持久化到数据库中',
  `IS_NONCONCURRENT` varchar(1) COLLATE utf8_bin NOT NULL COMMENT '是否并行,该属性可以通过注解配置',
  `IS_UPDATE_DATA` varchar(1) COLLATE utf8_bin NOT NULL,
  `REQUESTS_RECOVERY` varchar(1) COLLATE utf8_bin NOT NULL COMMENT '当一个scheduler失败后,其他实例可以发现那些执行失败的Jobs,若是1,那么该Job会被其他实例重新执行,否则对应的Job只能释放等待下次触发',
  `JOB_DATA` blob COMMENT '一个blob字段,存放持久化job对象',
  PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='存储每一个已配置的 Job 的详细信息';


CREATE TABLE `qrtz_triggers` (
  `SCHED_NAME` varchar(120) COLLATE utf8_bin NOT NULL COMMENT '调度器名,和配置文件org.quartz.scheduler.instanceName保持一致',
  `TRIGGER_NAME` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '触发器的名字',
  `TRIGGER_GROUP` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '触发器所属组的名字',
  `JOB_NAME` varchar(200) COLLATE utf8_bin NOT NULL COMMENT 'qrtz_job_details表job_name的外键',
  `JOB_GROUP` varchar(200) COLLATE utf8_bin NOT NULL COMMENT 'qrtz_job_details表job_group的外键',
  `DESCRIPTION` varchar(250) COLLATE utf8_bin DEFAULT NULL COMMENT '描述',
  `NEXT_FIRE_TIME` bigint(13) DEFAULT NULL COMMENT '下一次触发时间',
  `PREV_FIRE_TIME` bigint(13) DEFAULT NULL COMMENT '上一次触发时间',
  `PRIORITY` int(11) DEFAULT NULL COMMENT '线程优先级',
  `TRIGGER_STATE` varchar(16) COLLATE utf8_bin NOT NULL COMMENT '当前trigger状态',
  `TRIGGER_TYPE` varchar(8) COLLATE utf8_bin NOT NULL COMMENT '触发器类型',
  `START_TIME` bigint(13) NOT NULL COMMENT '开始时间',
  `END_TIME` bigint(13) DEFAULT NULL COMMENT '结束时间',
  `CALENDAR_NAME` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT '日历名称',
  `MISFIRE_INSTR` smallint(2) DEFAULT NULL COMMENT 'misfire处理规则,1代表【以当前时间为触发频率立刻触发一次,然后按照Cron频率依次执行】,
   2代表【不触发立即执行,等待下次Cron触发频率到达时刻开始按照Cron频率依次执行�】,
   -1代表【以错过的第一个频率时间立刻开始执行,重做错过的所有频率周期后,当下一次触发频率发生时间大于当前时间后,再按照正常的Cron频率依次执行】',
  `JOB_DATA` blob COMMENT 'JOB存储对象',
  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
  KEY `SCHED_NAME` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),
  CONSTRAINT `qrtz_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) REFERENCES `qrtz_job_details` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='存储已配置的 Trigger 的信息';


CREATE TABLE `qrtz_cron_triggers` (
  `SCHED_NAME` varchar(120) COLLATE utf8_bin NOT NULL COMMENT '集群名',
  `TRIGGER_NAME` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '调度器名,qrtz_triggers表trigger_name的外键',
  `TRIGGER_GROUP` varchar(200) COLLATE utf8_bin NOT NULL COMMENT 'qrtz_triggers表trigger_group的外键',
  `CRON_EXPRESSION` varchar(200) COLLATE utf8_bin NOT NULL COMMENT 'cron表达式',
  `TIME_ZONE_ID` varchar(80) COLLATE utf8_bin DEFAULT NULL COMMENT '时区ID',
  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
  CONSTRAINT `qrtz_cron_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='存放cron类型的触发器';

CREATE TABLE `qrtz_scheduler_state` (
  `SCHED_NAME` varchar(120) COLLATE utf8_bin NOT NULL COMMENT '调度器名称,集群名',
  `INSTANCE_NAME` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '集群中实例ID,配置文件中org.quartz.scheduler.instanceId的配置',
  `LAST_CHECKIN_TIME` bigint(13) NOT NULL COMMENT '上次检查时间',
  `CHECKIN_INTERVAL` bigint(13) NOT NULL COMMENT '检查时间间隔',
  PRIMARY KEY (`SCHED_NAME`,`INSTANCE_NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='调度器状态';


CREATE TABLE `qrtz_fired_triggers` (
  `SCHED_NAME` varchar(120) COLLATE utf8_bin NOT NULL COMMENT '调度器名称,集群名',
  `ENTRY_ID` varchar(95) COLLATE utf8_bin NOT NULL COMMENT '运行Id',
  `TRIGGER_NAME` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '触发器名',
  `TRIGGER_GROUP` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '触发器组',
  `INSTANCE_NAME` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '集群中实例ID',
  `FIRED_TIME` bigint(13) NOT NULL COMMENT '触发时间',
  `SCHED_TIME` bigint(13) NOT NULL COMMENT '计划时间',
  `PRIORITY` int(11) NOT NULL COMMENT '线程优先级',
  `STATE` varchar(16) COLLATE utf8_bin NOT NULL COMMENT '状态',
  `JOB_NAME` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT '任务名',
  `JOB_GROUP` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT '任务组',
  `IS_NONCONCURRENT` varchar(1) COLLATE utf8_bin DEFAULT NULL COMMENT '是否并行',
  `REQUESTS_RECOVERY` varchar(1) COLLATE utf8_bin DEFAULT NULL COMMENT '是否恢复',
  PRIMARY KEY (`SCHED_NAME`,`ENTRY_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息';


CREATE TABLE `qrtz_simple_triggers` (
  `SCHED_NAME` varchar(120) COLLATE utf8_bin NOT NULL COMMENT '调度器名,集群名',
  `TRIGGER_NAME` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '触发器名',
  `TRIGGER_GROUP` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '触发器组',
  `REPEAT_COUNT` bigint(7) NOT NULL COMMENT '重复次数',
  `REPEAT_INTERVAL` bigint(12) NOT NULL COMMENT '重复间隔',
  `TIMES_TRIGGERED` bigint(10) NOT NULL COMMENT '已触发次数',
  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
  CONSTRAINT `qrtz_simple_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='存储简单的 Trigger,包括重复次数,间隔,以及已触的次数';
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87

4、怎么使用

  • 当我们引入spring-boot-starter-quartz的依赖后,springboot在启动的时候会自动加载配置类:
  • org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration。
    会帮我们初始化好调度器,源码:SchedulerFactoryBean实现InitializingBean,重写afterPropertiesSet()
    里面就有了。
  • 而且Scheduler,Spring默认是帮我们启动的,不需要手动启动。

使用如下:

@Autowired
private Scheduler scheduler;
  • 1
  • 2

spring会自己注入,通过工厂类SchedulerFactoryBean的getObject()方法。 搞个基本的增删改查,很简单,代码也没怎么优化,就这样吧。

@Component
public class JobService {

    @Autowired
    private Scheduler scheduler;


    /**
     * 增加一个job
     *
     * @param jobClass
     *            任务实现类
     * @param jobName
     *            任务名称(建议唯一)
     * @param jobGroupName
     *            任务组名
     * @param cron
     *            时间表达式 (如:0/5 * * * * ? )
     * @param jobData
     *            参数
     */
    public void addJob(Class<? extends Job> jobClass, String jobName, String jobGroupName, String cron, Map jobData,Date endTime)
        throws SchedulerException {
        // 创建jobDetail实例,绑定Job实现类
        // 指明job的名称,所在组的名称,以及绑定job类
        // 任务名称和组构成任务key
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
        // 设置job参数
        if (jobData != null && jobData.size() > 0) {
            jobDetail.getJobDataMap().putAll(jobData);
        }
        // 定义调度触发规则
        // 使用cornTrigger规则
        // 触发器key
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
            .startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND))
            .withSchedule(CronScheduleBuilder.cronSchedule(cron)).startNow().build();
        if(endTime!=null){
            trigger.getTriggerBuilder().endAt(endTime);
        }
        // 把作业和触发器注册到任务调度中
        scheduler.scheduleJob(jobDetail, trigger);
    }



    public Trigger getTrigger(String jobName, String jobGroupName) throws SchedulerException {
        return scheduler.getTrigger(TriggerKey.triggerKey(jobName, jobGroupName));
    }

    /**
     * 删除任务一个job
     *
     * @param jobName
     *            任务名称
     * @param jobGroupName
     *            任务组名
     */
    public void deleteJob(String jobName, String jobGroupName) {
        try {
            scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 暂停一个job
     *
     * @param jobName
     * @param jobGroupName
     */
    public void pauseJob(String jobName, String jobGroupName) {
        try {
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 恢复一个job
     *
     * @param jobName
     * @param jobGroupName
     */
    public void resumeJob(String jobName, String jobGroupName) {
        try {
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            scheduler.resumeJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 立即执行一个job
     *
     * @param jobName
     * @param jobGroupName
     */
    public void runJobNow(String jobName, String jobGroupName) {
        try {
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            scheduler.triggerJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 修改 一个job的 时间表达式
     *
     * @param jobName
     * @param jobGroupName
     * @param cron
     */
    public void updateJob(String jobName, String jobGroupName, String cron,Map jobData) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
            Trigger trigger = scheduler.getTrigger(triggerKey);
            if(trigger instanceof CronTrigger){
                CronTrigger cronTrigger = (CronTrigger) trigger;
                cronTrigger = cronTrigger.getTriggerBuilder().withIdentity(triggerKey)
                        .withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
            }else {
                // 这里其实可以删掉在添加即可,懒得写了
                throw new RuntimeException("格式转换错误");
            }
            // 设置job参数
            if (jobData != null && jobData.size() > 0) {
                trigger.getJobDataMap().putAll(jobData);
            }
            // 重启触发器
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 和上面得基本demo没什么两样,就是scheduler 并不需要我们去创建了。
  • 当部署多个服务时,也不会重复执行。且任务会负载均衡分配。

5、时间演练

Quartz 提供了下一次运行的时间,我们可以通过下一次运行的时间,比对是否符合我们的预期

public class Test {
    public static void main(String[] args) throws ParseException, SchedulerException {
        String cron = "0/10 * * * * ?";
        CronExpression cronExpression = new CronExpression(cron);
        Date nextValidTimeAfter = cronExpression.getNextValidTimeAfter(new Date());
        System.out.println("nextValidTimeAfter=" + nextValidTimeAfter);
        nextValidTimeAfter = cronExpression.getNextValidTimeAfter(nextValidTimeAfter);
        System.out.println("nextValidTimeAfter=" + nextValidTimeAfter);
        nextValidTimeAfter = cronExpression.getNextValidTimeAfter(nextValidTimeAfter);
        System.out.println("nextValidTimeAfter=" + nextValidTimeAfter);
        CalendarIntervalTrigger calendarIntervalTrigger =
            newTrigger().withIdentity("trigger3", "group1")
                .withSchedule(calendarIntervalSchedule().withIntervalInWeeks(3)).build();

        Date nextFireTime = calendarIntervalTrigger.getFireTimeAfter(new Date());
        System.out.println("nextFireTime=" + DateUtils.formatTime(nextFireTime));
        nextFireTime = calendarIntervalTrigger.getFireTimeAfter(nextFireTime);
        System.out.println("nextFireTime=" + DateUtils.formatTime(nextFireTime));
        nextFireTime = calendarIntervalTrigger.getFireTimeAfter(nextFireTime);
        System.out.println("nextFireTime=" + DateUtils.formatTime(nextFireTime));
        nextFireTime = calendarIntervalTrigger.getFireTimeAfter(nextFireTime);
        System.out.println("nextFireTime=" + DateUtils.formatTime(nextFireTime));

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

每个 Tigger 都有一个Date getFireTimeAfter(Date afterTime) 的方法,返回的就是下一次运行的时间。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/40868
推荐阅读
相关标签
  

闽ICP备14008679号