Quartz框架汇总
目录
一.Quartz理论基础
(一)Timer
二.线程池
(一)ScheduledThreadPoolExcutor
(二)SingleThreadScheduledExecutor
二.quartz使用
(一) Job:封装成JobDetail设置属性
(二)Trigger:触发器:指定执行时间,开始结束时间等
(三)Scheduler:调度器,基于trigger的设定执行job
(四)JobDetailMap:保存任务实例的状态信息
三.Quartz集群原理
注意问题
四.Quartz实现异步通知
1. 一个Job绑定多个触发器
五.创建一个任务
(一)JobDetailFactoryBean或者CronTriggerFactoryBean
(二)使用builder创建Job或者Trigger
六.监听机制
1.JobListener
2.TriggerListener
3.SchedulerListener
七.基础代码实现
八.数据库各表及其作用
一.Quartz理论基础
(一)Timer
1.Quartz中的TaskQueue就是小顶堆的结构,用来存放timeTask
2.TimerThread:任务执行线程,死循环不断检查是否有有任务开始执行了,有就还是在这个线程执行它
3.单线程执行任务,有任务肯互相阻塞
schedule:执行任务超时,会导致后面的任务往后推移,预想在这个间隔内存存在的任务执行就不会执行了,就没有了
scheduleAtFixedRate:任务超时可能导致下一个任务马上开始执行
4.运行时异常会导致timer线程终止
5.任务调度是基于时间的,对系统时间敏感
二.线程池
(一)ScheduledThreadPoolExcutor
1.使用多线程执行任务,不会互相阻塞
2.如果线程失活,会新建线程执行任务
线程抛异常,任务会被丢弃,需要做捕获处理
3.DelayedWorkQueue:小顶堆,无界队列
在定时线程池当中,最大线程数是没有意义的
执行时间距离当前时间越近的任务在队列的前面
用于添加ScheduleFutureTask(继承于FutureTask,实现RunnableScheduledFuture接口)【提供异步执行的能力,并且可以返回执行】
线程池中的线程从DelayQueue中获取ScheduleFutureTask,然后执行任务
实现了Delayed接口,可以通过getDelay方法来获取延迟时间
4.Leader-Follower模式:避免没必要的唤醒和阻塞操作,这样会更加有效,且节省资源
5.应用场景:适用于多个后台线程执行周期性任务,同时为了满足资源管理的需求二需要限制后台线程数量
(二)SingleThreadScheduledExecutor
1.单线程的ScheduleThreadPoolExector
2.应用场景:适用于需要单个后台线程执行周期任务,同时需要保证任务顺序执行
二.quartz使用
(一) Job:封装成JobDetail设置属性
@DisallowConcurrentExecution //禁止并发的执行同一个job的定义的多个实例(jobDetial定义的)@PersistJobDataAfterExecution //持久化JobDetail中的JobDetailMap(对Trigger中的dataMap无效) //如果一个任务不是持久化的,则当没有触发器关联他的时候,Quartz会从scheduler中删除掉他boolean recovering = jobExecutionContext.isRecovering();//如果一个任务请求恢复,一般是该任务执行期间发生了系统崩溃或其他关闭进程的操作 //当服务再次启动的时候,会再次执行该任务,此时jobExecutionContext.isRecovering()会返回true
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class TestJob implements Job {@Overridepublic void execute(JobExecutionContext jobExecutionContext)throws JobExecutionException {}
}
(二)Trigger:触发器:指定执行时间,开始结束时间等
1.优先级:同时触发的Trigger之间才会比较优先级
如果trigger是可恢复的,再恢复后再调度时,优先级不变
2.错过触发:
判断misfire的条件:
(1)job到达触发时间没有被执行
(2)被执行的延迟时间超过Quartz配置的misfireThreshold阈值
可能原因:
1.当job达到触发时间时,所有的线程都被其他job占用,没有可用线程
2.在job需要触发的时间点,schedule停止了(可能是意外停止的)
3.job使用了@DisallowConcurrentExecution注解,job不能并发执行,当达到下一个job执行点的时候,上一个任务还没没有完成
4.job指定了过去开始的执行时间,例如当前时间是8:00:00指定开始的时间为7:00:00
策略:
1.默认使用 MISFIRE_INSTRUCTION_SMART_POLICY策略
2.SimpleTrigger:
1)now*相关的策略,会立即执行第一个misfire任务,同时修改startTime和repeatCount, 一次会重新计算finalFireTime,原计划时间会被打乱
2)next*相关的策略,不会立即执行misfire的任务,也不会修改startTime和repeatCount, 因此finalFireTime也没有发生改变,发生了misfire也是按照原计划进行执行
3.CronTrigger:
1)MISFIRE_INSTRUCTION_SMART_POLICY: Quartz不会判断发生了misfire,立即执行所有发生了misfire的任务,然后按照原计划进行执行。 例如:10:15分立即执行9:00和10:O0的任务,然后等待下一个任务在11:00执行,后续按照原计划执行。 2)MISFIRE_INSTRUCTION_FIRE_ONCE_NOW: 立即执行第一个发生misfire的任务,忽略其他发生misfire的任务,然后按照原计划继续执行。 例如:在10:15立即执行9:00任务,忽略10:00任务,然后等待下一个任务在11:00执行,后续按照原计划执行。 3)MISFIRE_INSTRUCTION_DO_NOTHING: 所有发生的misfire的任务都被忽略,只是按照原计划继续执行
calender: 用于排除时间段
SimpleTrigger:具体时间点,指定间隔重复执行
CronTrigger:cron表达式
(三)Scheduler:调度器,基于trigger的设定执行job
1.SchedulerFactory:
(1)创建Scheduler
(2)DirectSchedulerFactory:在代码里定制Schedule参数
(3)StdSchedulerFactory:读取classpath下的quartz.properties配置来实例化Scheduler
2.JobStore:
存储运行时信息的,包括Trigger,Scheduler,JobDetail,业务锁等
RAMJobStore(内存实现)
JobStoreTX(JDBC,事务由Quartz管理)
JobStoreCMT(JDBC,使用容器事务)
ClusteredJobStore(集群实现)
TerracottaJobStore(Terracotta中间件)
3.ThreadPool:SimpleThreadPool 、自定义线程池
(四)JobDetailMap:保存任务实例的状态信息
1.jobDetail:
默认只在Job被添加到调度程序(任务执行计划表)schedule的时候,存储一次关于该任务的状态信息数据,可以使用注解@PersistJobDataAfterExecution标明在一个任务执行完毕后就存储一次
2.trigger
任务被多个触发器引用的时候,根据不同的触发时机,可以输入不同的输入条件
所以,JobDataMap在Job和Trigger存储的数据并不一致。
三.Quartz集群原理
Quartz急群众每个节点是一个独立的Quartz任务应用,它又管理者其他节点。该集群需要分别对每个节点分别启动或停止,独立的Quartz节点并不与另一个节点或是管理节点通信。Quartz应用是通过共有相同数据库表来感知到另一应用。也就是说只有使用持久化JobStore存储Job和Trigger才能完成Quartz集群。
Quartz的集群部署方案是分布式的,没有负责集中管理的节点,而是利用数据库杭锁的方式来实现集群环境下的并发控制。
一个scheduler实例在集群模式下首先获取{0}LOCKS表中的行锁;
Quartz主要由两个行级锁。
lock_name | desc |
---|---|
STATE_ACCESS | 状态访问锁 |
TRIGGER_ACCESS | 触发器访问锁 |
Quartz集群争用触发器行锁,锁被占用只能等待,获取触发器行锁之后,先获取需要等待触发的其他触发器信息。数据库更新触发器状态信息,及时是否触发器行锁,供其他调度实例获取,然后在进行触发器任务调度操作,对数据库操作就要先获取行锁。
同一集群下,instanceName必须相同,instanceId可自动生成,isClustered为true,持久化存储,指定数据库类型对应的驱动类和数据源连接。
注意问题
1. 时间同步问题
Quartz实际并不关心你是在相同还是不同的机器上运行节点。当集群放置在不同的机器上时,称之为水平集群。节点跑在同一台机器上时,称之为垂直集群。对于垂直集群,存在着单点故障的问题。这对高可用性的应用来说是无法接受的,因为一旦机器崩溃了,所有的节点也就被终止了。对于水平集群,存在着时间同步问题。
节点用时间戳来通知其他实例它自己的最后检入时间。假如节点的时钟被设置为将来的时间,那么运行中的Scheduler将再也意识不到那个结点已经宕掉了。另一方面,如果某个节点的时钟被设置为过去的时间,也许另一节点就会认定那个节点已宕掉并试图接过它的Job重运行。最简单的同步计算机时钟的方式是使用某一个Internet时间服务器(Internet Time Server ITS)。
2. 节点争抢Job问题
因为Quartz使用了一个随机的负载均衡算法,Job以随机的方式由不同的实例执行。Quartz官网上提到当前,还不存在一个方法来指派(钉住) 一个 Job 到集群中特定的节点。
四.Quartz实现异步通知
1. 一个Job绑定多个触发器
一个Trigger只能绑定一个Job,但是一个Job可以绑定多个Trigger。
那么,若是我们将一个Job上绑定多个触发器,且每个触发器只是触发一次的话,那么,实际上可以实现阶梯式的异步通知,下面的基础代码实现中监听器StartApplicationListener就使用了异步输出。
五.创建一个任务
(一)JobDetailFactoryBean或者CronTriggerFactoryBean
JobDetailFactoryBean或者CronTriggerFactoryBean都实现了FactoryBean接口和InitializingBean接口。虽然我们new JobDetailFactoryBean(),但是实际上是将JobDetail交由的IOC管理。而InitializingBean接口会在属性装载完毕之后,自动的回调afterPropertiesSet()
方法,完成Bean对象的最终构建:
@Beanpublic JobDetailFactoryBean jobDetail(){//查询数据库或者配置文件JobDetailFactoryBean jobDetailFactoryBean=new JobDetailFactoryBean();jobDetailFactoryBean.setName("");jobDetailFactoryBean.setBeanName("");jobDetailFactoryBean.setJobClass((Class<? extends Job>) aClass);jobDetailFactoryBean.setGroup("");jobDetailFactoryBean.setDurability(true);return jobDetailFactoryBean;}
或者最后直接进行
JobDetail jobDetail= jobFactory.getObject();
(二)使用builder创建Job或者Trigger
// 定义任务调度实例, 并与TestJob绑定
JobDetail job = JobBuilder.newJob(TestJob.class).withIdentity("testJob", "testJobGroup")//调度器的名字和组别.usingJobData("job","jobDetail")//给调度器当中定义key并赋值,相当于参数,之后可以在job中取到.storeDurably() //表示当没有触发器与之关联时,仍然将job继续保存在Scheduler中.build();
// 定义触发器, 会马上执行一次, 接着5秒执行一次
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("testTrigger", "testTriggerGroup")//触发器的名字和分组.startNow()//定义好后马上就启动.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))//按照一定的策略触发.usingJobData("job","jobDedfafafafafdsfal")//给触发器当中定义key并赋值,相当于参数,之后可
以在job中取到//.repeatForever()表示一直执行.forJob(jobKey.getName(), jobKey.getGroup()) //制定Trigger和Job的关联关系.usingJobData(jobDataMap) //具体执行的方法中可以拿到这个传进去的信息。.build();
六.监听机制
Quartz的监听器用于当任务调度中关注的事件发生时,能够及时获取这一事件的通知。
- Quartz监听的种类:
- JobListener:任务监听;
- TriggerListener:触发器监听;
- SchedulerListener:调度器监听;
- 监听器的作用域
- 全局监听器:能够接收到所有的Job/Trigger的事件通知;
- 局部监听器:只能接收在其上注册Job或者Trigger的事件;
1.JobListener
public interface JobListener {//获取该JobListener的名称String getName();//Scheduler在JobDetail将要被执行时调用该方法void jobToBeExecuted(JobExecutionContext context);//Scheduler在JobDetail将要被执行时,但又被TriggerListener否决调用void jobExecutionVetoed(JobExecutionContext context);//任务执行完毕调用该方法void jobWasExecuted(JobExecutionContext context,JobExecutionException jobException);}
将JobListener绑定到Scheduler中:
//监听所有的Job
scheduler.getListenerManager().addJobListener(new SimpleJobListener(), EverythingMatcher.allJobs());
//监听特定的Job
scheduler.getListenerManager().addJobListener(new SimpleJobListener(), KeyMatcher.keyEquals(JobKey.jobKey("HelloWorld1_Job", "HelloWorld1_Group")));
//监听同一任务组的Job
scheduler.getListenerManager().addJobListener(new SimpleJobListener(), GroupMatcher.jobGroupEquals("HelloWorld2_Group"));
//监听两个任务组的Job
scheduler.getListenerManager().addJobListener(new SimpleJobListener(), OrMatcher.or(GroupMatcher.jobGroupEquals("HelloWorld1_Group"), GroupMatcher.jobGroupEquals("HelloWorld2_Group")));
2.TriggerListener
触发器监听,即在任务调度过程中,与触发器Trigger相关的事件:触发器触发、触发器未正常触发、触发器触发完成等。
public interface TriggerListener {//获取触发器的名字public String getName();//Job的execute()方法被调用时调用该方法。public void triggerFired(Trigger trigger, JobExecutionContext context);//Trigger触发后,TriggerListener给了一个选择否定Job的执行。假如该方法返回true,该Job将不会被触发public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context);//Trigger错过触发时间触发该方法,此方法不应该含有长时间的处理逻辑。public void triggerMisfired(Trigger trigger);//Trigger被触发并且完成Job后触发。public void triggerComplete(Trigger trigger, JobExecutionContext context,int triggerInstructionCode);
}
将TriggerListener绑定到Scheduler中:
//监听所有的Trigger
scheduler.getListenerManager().addTriggerListener(new SimpleTriggerListener("SimpleTrigger"), EverythingMatcher.allTriggers());
//监听特定的Trigger
scheduler.getListenerManager().addTriggerListener(new SimpleTriggerListener("SimpleTrigger"), KeyMatcher.keyEquals(TriggerKey.triggerKey("HelloWord1_Job", "HelloWorld1_Group")));
//监听一组Trigger
scheduler.getListenerManager().addTriggerListener(new SimpleTriggerListener("SimpleTrigger"), GroupMatcher.groupEquals("HelloWorld1_Group"));
//移除监听器
scheduler.getListenerManager().removeTriggerListener("SimpleTrigger");
3.SchedulerListener
SchedulerListener会在Scheduler的生命周期关键事件发生时调用。与Scheduler有关的事件包括:增加一个job/trigger,删除一个job/Trigger,scheduler发生严重错误,关闭Scheduler等。
public interface SchedulerListener {//用于部署JobDetail时调用public void jobScheduled(Trigger trigger);//用于卸载JobDetail时调用public void jobUnscheduled(String triggerName, String triggerGroup);//当一个Trigger没有触发次数时调用。public void triggerFinalized(Trigger trigger);public void triggersPaused(String triggerName, String triggerGroup);public void triggersResumed(String triggerName, String triggerGroup);//当一个或一组 JobDetail 暂停时调用这个方法。public void jobsPaused(String jobName, String jobGroup);//当一个或一组 Job 从暂停上恢复时调用这个方法。假如是一个 Job 组,jobName 参数将为 null。public void jobsResumed(String jobName, String jobGroup);//在 Scheduler 的正常运行期间产生一个严重错误时调用这个方法。public void schedulerError(String msg, SchedulerException cause);//当Scheduler 开启时,调用该方法public void schedulerStarted();//当Scheduler处于StandBy模式时,调用该方法public void schedulerInStandbyMode();//当Scheduler停止时,调用该方法public void schedulerShutdown();//当Scheduler中的数据被清除时,调用该方法。public void schedulingDataCleared();
}
将SchedulerListener绑定到Scheduler中:
//创建监听
scheduler.getListenerManager().addSchedulerListener(new SimpleSchedulerListener());
//移除监听
scheduler.getListenerManager().removeSchedulerListener(new SimpleSchedulerListener());
七.基础代码实现
Start.java
@SpringBootApplication
public class Start {public static void main(String[] args) {SpringApplication.run(Start.class,args);}
}
QuartzConfiguration.java
@Configuration
public class QuartzConfiguration {@Autowiredprivate DataSource dataSource;@Beanpublic Scheduler scheduler() throws IOException {return schedulerFactoryBean().getScheduler();}@Beanpublic Properties quartzProperties() throws IOException {PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();propertiesFactoryBean.setLocation(new ClassPathResource("/application.yml"));propertiesFactoryBean.afterPropertiesSet();return propertiesFactoryBean.getObject();}@Beanpublic Executor schedulerThreadPool(){ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors());executor.setQueueCapacity(Runtime.getRuntime().availableProcessors());return executor;}@Beanpublic SchedulerFactoryBean schedulerFactoryBean() throws IOException {SchedulerFactoryBean factory = new SchedulerFactoryBean();factory.setSchedulerName("cluster_scheduler");factory.setDataSource(dataSource);factory.setApplicationContextSchedulerContextKey("application");factory.setQuartzProperties(quartzProperties());factory.setTaskExecutor(schedulerThreadPool());factory.setStartupDelay(10);return factory;}
}
StartApplicationListener.java
@Component
//@Controller也可以
public class StartApplicationListener implements ApplicationListener<ContextRefreshedEvent> {@Autowiredprivate Scheduler scheduler;@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {try {TriggerKey triggerKey1 = TriggerKey.triggerKey("trigger1","group1");Trigger trigger1 = scheduler.getTrigger(triggerKey1);if (trigger1 ==null){trigger1 = TriggerBuilder.newTrigger().withIdentity(triggerKey1).withSchedule(CronScheduleBuilder.cronSchedule("0/3 * * * * ?"))//cron表达式,每个10秒执行一次.build();JobDetail jobDetail = JobBuilder.newJob(TestJob.class).withIdentity("job1","group1").build();scheduler.scheduleJob(jobDetail,trigger1);}TriggerKey triggerKey2 = TriggerKey.triggerKey("trigger2","group2");Trigger trigger2 = scheduler.getTrigger(triggerKey2);if (trigger2 ==null) {trigger2 = TriggerBuilder.newTrigger().withIdentity(triggerKey2).withSchedule(CronScheduleBuilder.cronSchedule("0/3 * * * * ?"))//cron表达式,每个10秒执行一次.build();JobDetail jobDetail = JobBuilder.newJob(TestJob.class).withIdentity("job2", "group2").build();scheduler.scheduleJob(jobDetail, trigger2);}scheduler.start();} catch (SchedulerException e) {e.printStackTrace();}}
}
从Start.java程序入口进入程序,俩个trigger一起异步输出如下
//间隔3s输出一次
开启时间 :2022-10-28 16:50:27
开启时间 :2022-10-28 16:50:27
开启时间 :2022-10-28 16:50:30
开启时间 :2022-10-28 16:50:30
开启时间 :2022-10-28 16:50:33
开启时间 :2022-10-28 16:50:33
开启时间 :2022-10-28 16:50:36
开启时间 :2022-10-28 16:50:36进程已结束,退出代码为 -1
八.数据库各表及其作用
1.qrtz_job_details:记录每个任务的详细信息。 2.qrtz_triggers:记录每个触发器的详细信息。 3.qrtz_corn_triggers:记录cornTrigger的信息。 4.qrtz_scheduler_state:记录 调度器(每个机器节点)的生命状态。 5.qrtz_fired_triggers:记录每个正在执行的触发器。 6. qrtz_simple_triggers:存储简单的trigger,包括重复次数,间隔,以及触发次数。 7. qrtz_simprop_triggers:存储CalendarIntervalTrigger和DailyTimeIntervalTrigger两种类型的触发器, 8. qrtz_blob_triggers:自定义的triggers使用blog类型进行存储,非自定义的triggers不会存放在此表中, Quartz提供的triggers包括:CronTrigger,CalendarIntervalTrigger,DailyTimeIntervalTrigger以及SimpleTrigger。 9. qrtz_calendars:以 Blob 类型存储 Quartz 的 Calendar 信息 10. qrtz_paused_trigger_grps:存储已暂停的 Trigger 组的信息 11.qrtz_locks:记录程序的悲观锁(防止多个节点同时执行同一个定时任务)。
本笔记基于图灵的课堂和https://www.jianshu.com/p/ece144448134这位大佬的笔记
Quartz框架汇总相关推荐
- 前端Js框架 UI框架汇总 特性 适用范围 选择
身为一个资深后端工程师,面对层出不穷的前端框架,总让人眼花缭乱,做一个综合解析贴,从全局着眼,让我们明白各种前端框架的应用范围,为如何选择前端框架,从不同的维度提供一些线索,做为一个长期优化贴,欢迎指 ...
- 2018年终总结之AI领域开源框架汇总
2018年终总结之AI领域开源框架汇总 [稍显活跃的第一季度] 2018.3.04--OpenAI公布 "后见之明经验复现(Hindsight Experience Reply, HER)& ...
- Quartz 框架快速入门(二)
尽可能的用声明式处理软件配置,其次才考虑编程式的方式.在上一篇<Quartz 框架快速入门(一)>中,如果我们要在 Job 启动之后改变它的执行时间和频度,必须去修改源代码重新编译.这种方 ...
- Quartz框架多个trigger任务执行出现漏执行的问题分析--转
原文地址:http://blog.csdn.net/dailywater/article/details/51470779 一.问题描述 使用Quartz配置定时任务,配置了超过10个定时任务,这些 ...
- Quartz 框架快速入门(四)
Spring的scheduling.quartz包中对Quartz框架进行了封装,使得开发时不用写任何QuartSpring的代码就可以实现定时任务.Spring通过JobDetailBean,Met ...
- Quartz 框架快速入门(三)
在前面两篇文章中简单介绍了在java应用程序中如何使用Quartz框架,这一篇中我们将看到如何在web环境下通过配置文件来完成Quartz的后台作业调度,而不必手工去创建Trigger和Schedul ...
- SpringBoot与quartz框架实现分布式定时任务
前言 quartz的分布式调度策略是以数据库为边界资源的一种异步策略.各个调度器都遵守一个基于数据库锁的操作规则从而保证了操作的唯一性. 在quartz的集群解决方案里有张表scheduler_loc ...
- Quartz框架学习(1)—核心层次结构
Quartz框架学习 Quartz(任务调度)框架的核心组件: job:任务.即任务调度行为中所要调度的对象. trigger:触发器.是什么促使了一个任务的调度?当然是时间.这也算事件驱动类型程序. ...
- Quartz框架应用(1)
为什么80%的码农都做不了架构师?>>> 在你使用调度器之前,需要借助一些具体的例子去理解. 你可以使用SchedulerFactory类来达到程序调度的目的. 有一些Quar ...
最新文章
- python小白逆袭大神课程心得_Python小白逆袭大神学习心得
- linux进程--写时拷贝技术copy-on-write(七)
- DB2 格式化输出 Date
- 安装node,vue编译环境
- 【UVA11324】The Largest Clique (SCC)
- 《汇编语言(第三版)》标志寄存器
- 使用-辗转相除法-求最大公约数
- 《ggplot2:数据分析与图形艺术》,读书笔记
- easy-hiphop一键安装hiphop脚本
- 从入门到精通 - Fayson带你玩转CDH
- 编码的奥秘:字节与十六进制
- 大神总结的一套PCB学习方法! 真得很受用!
- 远程办公软件华为云WeLink视频会议指南(下篇:记录会议纪要)
- 阿里云iconfont使用方法
- 【Unifying Motion Deblurring and Frame Interpolation with Events】阅读笔记
- Hadoop-大数据引言
- 便利贴--23{移动端滑动模块-改良}
- 清理电脑C盘空间的脚本
- python 复制替换文件_在Python中复制和替换文件
- 啊哈c语言有函数么,啊哈c-啊哈c为什么不能运行??如图