`

深入解读Quartz及应用

 
阅读更多

一、Quartz基本概念

 

Quartz 是 OpenSymphony开源组织在任务调度领域的一个开源项目,完全基于 Java 实现。该项目于 2009 年被 Terracotta 收购,目前是 Terracotta 旗下的一个项目。读者可以到 http://www.quartz-scheduler.org/站点下载 Quartz 的最新发布版本及其源代码。QCT系统使用的是版本 1.6.0,因此本文内容基于该版本。本文不仅介绍如何应用 Quartz 进行开发,也对其内部实现原理作一定讲解。

 

二、Quartz的特点

Ø强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求;

 

Ø灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式;

 

Ø分布式和集群能力,支持分布式和集群环境中应用Quartz进行任务调度。

 

ØQuartz能够以独立的方式运行(在它自己的Java虚拟机中),可以通过RMI使用Quartz
 
三、核心元素
ØJob:是一个接口,此接口只有一个方法,void exceuteJobExecutionContext context) ,开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中;
ØJobDetailQuartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。因此需要通过一个类描述Job的实现类及其相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色;
 
ØTrigger:触发器,描述触发Job执行的时间触发规则。主要有SimpleTrigger  CronTriggerNthIncludeDayTrigger三种触发器,当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择。而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案,如每天中午12执行等;
ØCalendarorg.quartz.Calendarjava.util.Calendar不同,它是一些日历特定时间点的集合。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点;
ØScheduler:代表一个Quartz的独立运行容器,TriggerJobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一(可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler可以将Trigger绑定到某一JobDetail中,这样当 Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。Scheduler主要有三种:RemoteMBeanSchedulerRemoteSchedulerStdSchedulerScheduler拥有一个SchedulerContext,它类似ServeletContext,保存着Scheduler上下文信息,注册到该SchedulerJobTrigger都可以访问SchedulerContext内的信息。SchedulerContext内部通过一个Map,以键值对的方式维护这些上下文数据;
ØListenerQuartz提供了listener功能。主要包括三种listenerJobListenerTriggerListenerSchedulerListener。可对JobTriggerScheduler内部行为进行监听;
ØThreadPoolScheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。
 
四、Quartz 任务调度的基本实现原理
Quartz中,Job用于表示被调度的任务。主要有两种类型的Job:无状态(sateless)和有状态的(stateful)。对于同一个Trigger来说,有状态的Job不能被并行执行,只有上一次触发的任务被执行完之后,才能触发下一次执行。Job主要有两种属性:volatilitydurability,其中volatility表示任务是否被持久化到数据库存储,而durability表示在没有Trigger关联的时候任务是否被保留。两者都是在值为true的时候任务被持久化或保留。Scheduler内部组件图:
Quartz核心元素关系图


 Quartz中,有两类线程,Scheduler调度线程和任务执行线程,其中任务执行线程通常使用一个线程池维护一组线程。线程图如下:


 Scheduler调度线程主要有两个:执行常规的线程和执行misfired trigger的线程。常规调度线程轮询存储的所有Trigger,如果有需要触发的trigger,即到达了下一个触发时间,则从任务执行线程池中获取一个空闲线程,执行与该trigger关联的任务。Misfire线程是扫描所有的trigger,查看是否有misfired trigger,如果有就根据misfire的策略分别处理。下图描述了这两个线程的基本流程
Quartz调度线程流程图

 
五、 通过源码了解如何实现按时间调度
Quartz最核心的地方是QuartzSchedulerThread运行机制。下面解析下它的run方法:

以上是run的最开头的一段,不难看出这是在等待schedulerstart,实际上Quartz就是通过线程的waitsleep来实现时间调度。继续看代码:

 这段代码是从jobStore里拿到下一个要执行的trigger,一般情况下jobStore使用的是RAMJobStore,即trigger等相关信息存放在内存里,如果需要把任务持久化就得使用可持久化JobStore。继续看代码:

此段代码是计算下一个trigger的执行时间和现在系统时间的差,然后通过循环线程sleep的方式暂停住此线程,一直等到trigger的执行时间点。继续看代码:

此段代码就是包装trigger,然后通过以JobRunShell为载体,在threadpool里执行trigger所关联的jobDetail
 
六、 数据存储
Quartz中的triggerjob需要存储下来才能被使用。Quartz中有两种存储方式:RAMJobStore,JobStoreSupport,其中RAMJobStore是将triggerjob存储在内存中,而JobStoreSupport是基于jdbctriggerjob存储到数据库中。RAMJobStore的存取速度快,但缺点是其在系统被停止后所有的数据都会丢失。而JobStoreSupport使用一个驱动代理StdJDBCDelegate来操作triggerjob数据存储到数据库。StdJDBCDelegate实现了大部分基于标准JDBC的功能接口,但是对于各种数据库来说,需要根据其具体实现的特点做某些特殊处理,因此不同的数据库需要扩展StdJDBCDelegate以实现这些特殊处理。Quartz已自带了一些数据库的扩展实现,可以直接使用,如下图:

 Quartz数据表

 七、 Quartz与Spring集成
方法一、 Spring org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean类,使用此方法使开发人员对Quartz完全透明,需要实现定时任务的方法只是一个普通方法。

 第一种方式的Java代码:

 方法二、借助于Springorg.springframework.scheduling.quartz.JobDetailBean的类功能,继承 Spring封装Quartzorg.springframework.scheduling.quartz.QuartzJobBean,实现 executeInternal方法,executeInternal方法中调用业务类。

 第二种方式的Java代码:

 八、 企业级开发中常见应用

在应用Quartz进行企业级的开发是,有些问题会经常遇到。下面介绍企业开发中常见的一些问题及通常的解决办法:

  应用一:如何使用不同类型的Trigger

  前面我们提到Quartz中三种类型的TriggerSimpleTriggerCronTriggerNthIncludedDayTrigger

  SimpleTrigger一般用于实现每隔一定时间执行任务,以及重复多少次,如每2小时执行一次,重复执行5次。SimpleTrigger内部实现机制是通过计算间隔时间来计算下次的执行时间,这就导致其不合适调度定时的任务。例如我们想每天的8:00AM执行任务,如果使用SimpleTrigger的话间隔时间就是一天。主要这里就有一个问题,即当有misfired的任务并且恢复执行时,该执行时间是随机的(取决于何时执行misfired的任务,例如某天的9:00PM)。这会导致之后每天的执行时间都会变成9:00PM,而不是我原来期望的8:00AM

 

   CronTrigger类似于Linux上的任务调度命令crontab,即利用一个包含7个字段的表达式来表示时间调度方式。例如,“0 15 10 * * ? *”表示每天的10:15AM执行任务。对于涉及到星期和月份的调度,CronTrigger是最合适的,甚至某些情况下是唯一选择。例如,“0 10 14 3 WED”表示三月份的每个星期三的下午14:10PM执行任务。使用者可以在具体用的该trigger时再详细了解每个字段的含义。

 

   NthIncludedDayTrigger的用途比较简单明确,即用于每间隔一个周期的第几天调度任务,例如,每个月的第2天执行指定的任务。

 

 

应用二:使用有状态(StatefulJob)还是无状态的任务(Job)

   Quartz中,Job是一个接口,企业应用需要实现这个接口定义自己的任务。基本来说,任务分为有状态和无状态两种。实现Job接口的任务缺省为无状态的。Quartz中还有另外一个接口StatefulJob。实现StatefulJob接口的任务为有状态的,下图列出了QuartzJob接口的定义以及一些自带的实现类:

无状态任务一般指可以并发的任务,即任务之间是独立的不会相互干扰。例如我们定义一个trigger,每2分钟执行一次,但是某些情况下一个任务可能需要3分钟才能执行完,这样,在上一个任务还处在执行状态时,下一次触发时间已经到了。对于无状态任务,只要触发时间到了就会被执行,因为几个相同任务可以并发执行。但是对于有状态任务来说,是不能并发执行的,同一时间只能有一个任务在执行。

      如某些任务需要对数据库中的数据进行增删改处理。这些任务不能并发执行,否则会造成数据混乱。因此我们使用StatefulJob接口。现在回到上面的例子,任务每 2 分钟执行一次,若某次任务执行了 5 分钟才完成,Quartz 会怎么处理呢?按照 trigger 的规则,第 2 分钟和第 4 分钟分别会有一次预定的触发执行,但是由于是有状态任务,因此实际不会被触发。在第 5 分钟第一次任务执行完毕时,Quartz 会把第 2 和第 4 分钟的两次触发作为 misfired job 进行处理。对于 misfired jobQuartz 会查看其 misfire 策略是如何设定的,如果是立刻执行,则会马上启动一次执行,如果是等待下次执行,则会忽略错过的任务,而等待下(即第 6 分钟)触发执行。

 

 

应用三:如何设置Quartz的线程池和并发任务

nQuartz中自带了一个线程的实现:SimpleThreadPool。类如其名,这只是线程池的一种简单实现,没有提供动态自发调整等高级特性。
nQuartz提供了一个配置参数: org.quartz.threadPool.threadCount,可以在初始化时设定线程池的线程数量,但是一次设定后不能再修改。假定这个数目是 10,则在并发任务达到 10 个以后,再有触发的任务就无法被执行了,只能等待有空闲线程的时候才能得到执行。因此有些 trigger 就可能被 misfire。但是必须指出一点,这个初始线程数并不是越大越好。当并发线程太多时,系统整体性能反而会下降,因为系统把很多时间花在了线程调度上。根据一般经验,这个值在 10 -- 50 比较合适。
n对于一些注重性能的线程池来说,会根据实际线程使用情况进行动态调整,例如初始线程数,最大线程数,空闲线程数等。在使用者应用中,如果有更好的线程池,则可以在配置文件中通过下面参数替换 SimpleThreadPoolorg.quartz.threadPool.class = myapp.GreatThreadPool

 

应用四:如何处理Misfired任务

   Quartz应用中,misfired job是经常遇到的情况,一般来说,下面这些原因可能造成misfired job

   1)系统因为某些原因被重启。在系统关闭到重启之间的一段时间里,可能有些任务会被misfire

   2Trigger被暂停(pause)的一段时间里,有些任务可能会被misfire

   3)线程池中所有线程都被占用,导致任务无法被触发执行,造成misfire

   4)有状态任务在下次触发时间到达时,上次执行还没有结束;

   为了处理misfired jobQuartz中为trigger定义了处理策略,主要有下面两种:

   MISFIRE_INSTRUCTION_FIRE_ONCE_NOW:针对 misfired job 马上执行一次;

   MISFIRE_INSTRUCTION_DO_NOTHING:忽略 misfired job,等待下次触发。

  • 大小: 15.9 KB
  • 大小: 5.2 KB
  • 大小: 7.9 KB
  • 大小: 30.6 KB
  • 大小: 23.8 KB
  • 大小: 42.3 KB
  • 大小: 42.1 KB
  • 大小: 19.1 KB
  • 大小: 3.8 KB
  • 大小: 12.1 KB
  • 大小: 61.2 KB
  • 大小: 8.5 KB
  • 大小: 73.1 KB
  • 大小: 13.9 KB
  • 大小: 7.8 KB
分享到:
评论

相关推荐

    深入解读Quartz的原理

    ### 深入解读Quartz的原理 #### 一、Quartz概述 Quartz 是一个功能强大且易于使用的 Java 开源定时任务调度器。它能够触发在指定的时间执行任务(通常称为作业)。Quartz 能够满足从简单的到非常复杂的业务场景...

    quartz源码解析(一)

    Quartz是Java领域的一款强大的任务调度框架,常用于在应用程序中安排周期性的任务执行。它提供了灵活的时间表定义,支持简单任务和复杂的作业链,并且能够与其他Java应用服务器集成。这篇博客“quartz源码解析(一)”...

    Quartz Job Scheduling Framework第11章翻译初稿

    在第11章的翻译初稿中,我们可能会深入探讨Quartz的核心概念、设计模式以及如何在实际项目中应用它。下面是对这个章节内容的详细解读。 一、Quartz简介 Quartz是Java领域的一个强大且灵活的任务调度库,它能够管理...

    定时器quartZ

    【定时器quartz】是一种广泛应用于Java环境中的开源任务调度框架,它允许程序在特定时间执行预定的任务。Quartz的核心功能是创建、管理和执行作业(Job)和触发器(Trigger)。作业代表要执行的任务,而触发器则定义...

    Quzart开发demo

    这个"Quzart开发demo"包含了一系列的资源,帮助开发者深入理解和实践Quartz的开发与配置。以下是对这些资源内容的详细解读: 1. **Quartz开发指南.pdf**: 这份PDF文档通常会提供Quartz的全面介绍,包括其核心概念...

    SLitraniSim:内置在rootSLitrani中的简单Quartz和PMT检测器

    Quartz是一种透明度极高的材料,常用于光学应用,尤其是作为闪烁体。PMT则是一种能够将光信号转换为电信号的高灵敏度探测器,对于低强度光信号有很好的响应。 2. **光传播模拟**:该工具能够模拟光子在Quartz等介质...

    电子-一种电化学阻抗和石英微天平联用的分析装置及方法

    一种电化学阻抗和石英微天平联用的分析装置及方法”涉及的是电子技术与分析仪器的交叉领域,具体来说,是电化学阻抗谱(Electrochemical Impedance Spectroscopy, EIS)与石英晶体微天平(Quartz Crystal ...

    JAVA开发JAVA办公自动化系统(源代码+论文+外文翻译)

    译文可能会包含对原论文的深入解读,或者对所用技术的详细解释。 总的来说,这个资源包为Java开发者提供了一个完整的OA系统实例,不仅可以作为学习Java编程和企业级应用开发的实战案例,还可以帮助理解办公自动化...

    spring开发参考手册.rar

    这份《Spring开发参考手册》将帮助开发者深入理解Spring框架的各个组件,学习如何利用它们构建高质量、可扩展的Java应用。PDF文档中应该包含详细的API说明、示例代码和最佳实践,是Spring开发者必备的参考资料。通过...

    iphone开发秘籍2版-13~21章源码

    《iPhone开发秘籍第二版》是一本深入探讨iOS应用程序开发的专业书籍,涵盖了从第13章到第21章的源代码。这些章节通常涉及高级iOS开发技术,包括UI设计、网络编程、数据存储、多线程、动画、游戏开发以及性能优化等...

    千锋ios培训杂项

    以下是对这些知识点的详细解读: 1. **地图**:在iOS应用开发中,地图功能是不可或缺的。苹果提供了自家的地图服务(MapKit框架),开发者可以利用它来集成地图到应用程序中,实现定位、导航、路径规划等功能。...

    278Springboot实现的心理测评管理系统.zip

    最后,报告生成模块将分析结果以可视化的方式呈现给用户,以便于理解和解读。 在技术实现上,系统可能会采用以下组件: 1. **数据库管理**:Springboot与MyBatis或JPA结合,可以方便地操作数据库,如MySQL、Oracle...

    Spring Boot Cookbook(2015.09)源码

    《Spring Boot Cookbook(2015.09)源码》是针对Spring Boot框架的一份实践性教程,包含了丰富的代码示例,旨在帮助开发者深入理解并掌握Spring Boot的核心特性和应用。Spring Boot是由Pivotal团队开发的一个Java框架...

    spring2.5 定时器任务

    本文将详细介绍Spring2.5中的定时器任务配置方法,并通过示例代码进行深入解析。 #### 二、定时任务的核心组件介绍 在Spring2.5中,定时任务主要依赖以下几个核心组件: 1. **`ThreadPoolTaskExecutor`**:这是...

    IQAN_IQANScanCANBUS_TheBus_

    IQAN(Interactive Quartz Application Network)是一个先进的监控和控制系统的品牌,常用于重型机械设备如卡车、挖掘机等。CANBUS(Controller Area Network Bus)则是一种串行通信协议,广泛应用于汽车电子系统,...

    Job Plus项目是基于SpringBoot+Vue的轻量级定时任务管理系统.zip

    - 源码分析有助于深入理解SpringBoot和Vue.js的结合使用,以及如何设计和实现一个完整的Web应用。 6. **技术栈拓展**: - 数据库:可能使用MySQL、PostgreSQL等,配合JPA或MyBatis进行数据访问操作。 - 安全:...

    Cron表达式解析 翻译为中英文.zip

    Cron表达式是Unix/Linux系统中的定时任务调度器Cron所使用的语法,也被广泛应用于Java世界,例如Quartz、Spring等框架。它允许用户以字符串的形式定义任务的执行时间,如分钟、小时、日期等。这个压缩包文件包含了对...

    Java开发典型模块大全(仅含程序源码)-20个Java项目

    Java开发典型模块大全,这是一份专为Java开发者准备的宝贵资源,包含了20个不同类型的Java项目,每个项目都提供了完整的程序源码,旨在帮助开发者深入理解和掌握Java编程的各种核心概念和技术。以下是对这些模块及其...

    25个经典的Spring面试问题包含答案

    以下是对这些问题的详细解读: 1. **什么是Spring框架?** Spring是一个开源的Java平台,它为构建企业级应用提供了全面的框架支持,包括依赖注入、面向切面编程(AOP)、数据访问、事务管理等。 2. **依赖注入...

Global site tag (gtag.js) - Google Analytics