首页 > 其他分享 >Quartz与SpringBoot 搞定任务调度

Quartz与SpringBoot 搞定任务调度

时间:2023-02-25 13:01:41浏览次数:36  
标签:COMMENT bin Quartz SpringBoot utf8 NULL COLLATE 任务调度 NAME


一、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

发生Misfire 对于其他的Trigger

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

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

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>

在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);
}
}
}

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

二、与Springboot整合

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

1、引入依赖

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

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
  • 这里打算搞个集群版的,就算你部署多台服务器,定时任务会自己负载均衡,不会每台服务器都执行。
  • 表的生成,其实可以修改配置,启动的时候自己在数据库生成表。操作方法:
  • 修改: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

重要表字段解析

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,包括重复次数,间隔,以及已触的次数';

4、怎么使用

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

使用如下:

@Autowired
private Scheduler scheduler;

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();
}
}
}
  • 和上面得基本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));

}
}

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


标签:COMMENT,bin,Quartz,SpringBoot,utf8,NULL,COLLATE,任务调度,NAME
From: https://blog.51cto.com/u_15461374/6085313

相关文章

  • 基于springboot的restful接口的请求地址含有特殊字符被项目自带tomcat拦截问题
    写了一个基于springboot的restful接口,但按照设计,请求地址十分古怪,类似这样:http://192.168.10.8:8080/?skey=fb5e8ea9249c4ac19dad5e2a341e09ce&filter[]=A3011300500,eq,350......
  • SpringBoot-使用链接字符串动态创建SqlSessionFactory执行任意SQL脚本
    SpringBoot-使用链接字符串动态创建SqlSessionFactory执行任意SQL脚本引言SpringBoot大大减少了使用XML配置的复杂性,但是想通过代码去实例化一个对象有点儿无从下手的感觉。......
  • 利用Github Action自动化部署SpringBoot项目
    环境准备一台拥有公共IP的1核1G以上配置的Linux云服务器。笔者选用的Linux发行版为CentOSLinuxrelease7.0.1406(Core)已安装好JDK8与Maven。若是还未安装,可以参考以......
  • Spark系列 - (4) Spark任务调度
    目前已经更新完《Java并发编程》,《Spring核心知识》《Docker教程》和《JVM性能优化》,都是多年面试总结。欢迎关注【后端精进之路】,轻松阅读全部文章。Java并发编程:Java......
  • 基于SpringBoot+Vue开发的前后端分离博客项目一一后端开发
    @目录前言一、Java后端接口开发1.新建SpringBoot项目1.1开发技术栈:1.2pom中jar包引入:1.3配置文件:2.整合mybatisPlus2.1引入pom的jar包2.2配置分页插件、代码生成......
  • SpringBoot31 - 监控
    监控​ 在说监控之前,需要回顾一下软件业的发展史。最早的软件完成一些非常简单的功能,代码不多,错误也少。随着软件功能的逐步完善,软件的功能变得越来越复杂,功能不能得到有......
  • SpringBoot29 - RocketMQ
    SpringBoot整合RocketMQ​ RocketMQ由阿里研发,后捐赠给apache基金会,目前是apache基金会顶级项目之一,也是目前市面上的MQ产品中较为流行的产品之一,它遵从AMQP协议。安装......
  • SpringBoot30 - 整合Kafka
    SpringBoot整合Kafka安装​ windows版安装包下载地址:https://kafka.apache.org/downloads​ 下载完毕后得到tgz压缩文件,使用解压缩软件解压缩即可使用,解压后得到如下......
  • SpringBoot入门
    SpringBoot提供2Controller与@RestController两者分别用于标记此类负责接收及处理HTTP请求路径映射:@RequestMapping的value属性用于匹配URL映射,value支持简单表达式@Re......
  • springboot如何编写
    最佳实践引入场景依赖https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter1查看自动配置了那些(选)1.自行......