Quartz学习总结
一、基本介绍:
Quartz 是 OpenSymphony 开源组织在任务调度领域的一个开源项目,完全基于 Java 实现。该项目于 2009 年被 Terracotta 收购,目前是 Terracotta 旗下的一个项目。读者可以到 http://www.quartz-scheduler.org/站点下载 Quartz 的发布版本及其源代码。
Quartz 具有以下特点:
1、强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求;
2、灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式;
3、分布式和集群能力,Terracotta 收购后在原来功能基础上作了进一步提升。
这些总结起来就是:在某一个有规律的时间点干某件事。并且时间的触发的条件可以非常复杂(比如每月最后一个工作日的17:50),复杂到需要一个专门的框架来干这个事。 Quartz就是来干这样的事,你给它一个触发条件的定义,它负责到了时间点,触发相应的Job起来干活。
二、POM引用:
1 <dependency> 2 <groupId>org.quartz-scheduler</groupId> 3 <artifactId>quartz</artifactId> 4 <version>2.2.3</version> 5 </dependency> 6 <dependency> 7 <groupId>org.quartz-scheduler</groupId> 8 <artifactId>quartz-jobs</artifactId> 9 <version>2.2.3</version> 10 </dependency>
三、基本结构:
(一)核心元素:
1、Scheduler:一个scheduler的生命周期是有限的,从通过SchedulerFactory创建开始直到调用它的shutdown()方法。scheduler接口一旦创建,就能添加、移除、和列出job和trigger,并执行另一些与调度相关的操作(例如暂停trigger),然而,在scheduler被start()方法启动前,它不会作用于任何trigger(执行job)。。
2、Job: 一个job就是实现了Job接口的类。如下所示,这个接口只有一个简单的方法:execute;
3、JobDetail: JobDetail对象由Quartz客户端(你的程序)在job添加进scheduler的时候创建。这个对象包含了很多job的属性设置,和JobDataMap一样,它能够存储你的job实例的状态信息。JobDetail对象本质上是定义的job实例。
4、Trigger:
(1) trigger对象常常用于触发job的执行(或者触发),当你希望安排一个job,你可以实例化一个trigger,并调整其属性来提供你想要的调度。trigger可能有一个与它们关联的JobDataMap对象。JobDataMap用于传递特定于触发器触发的工作参数。Quartz附带一些不同的触发类型,但是最常用的就是SimpleTrigger和CronTrigger。
(2) SimpleTrigger很方便,如果你需要一次性执行(只是在一个给定时刻执行job),或者如果你需要一个job在一个给定的时间,并让它重复N次,并在执行之间延迟T。
(3) CronTrigger是有用的,如果你想拥有引发基于当前日历时间表,如每个星期五,中午或在每个月的第十天 10:15。
5、JobBuilder: 用于定义/构建已经定义了Job实例的JobDetail实例。
6、TriggerBuilder: 用于定义/构建Trigger实例。
(二)数据表说明:
序号 |
表名 |
说明 |
1 |
QRTZ_CALENDARS |
存储Quartz的Calendar信息 |
2 |
QRTZ_CRON_TRIGGERS |
存储CronTrigger,包括Cron表达式和时区信息 |
3 |
QRTZ_FIRED_TRIGGERS |
存储与已触发的Trigger相关的状态信息,以及相联Job的执行信息 |
4 |
QRTZ_PAUSED_TRIGGER_GRPS |
存储已暂停的Trigger组的信息 |
5 |
QRTZ_SCHEDULER_STATE |
存储少量的有关Scheduler的状态信息,和别的Scheduler实例 |
6 |
QRTZ_LOCKS |
存储程序的悲观锁的信息 |
7 |
QRTZ_JOB_DETAILS |
存储每一个已配置的Job的详细信息 |
8 |
QRTZ_SIMPLE_TRIGGERS |
存储简单的Trigger,包括重复次数、间隔、以及已触的次数 |
9 |
QRTZ_BLOG_TRIGGERS |
Trigger作为Blob类型存储 |
10 |
QRTZ_TRIGGERS |
存储已配置的Trigger的信息 |
11 |
QRTZ_SIMPROP_TRIGGERS |
|
(三)关系图:
Quartz 任务调度的核心元素是 scheduler, trigger 和 job,其中 trigger 和 job 是任务调度的元数据, scheduler 是实际执行调度的控制器。
在 Quartz 中,trigger 是用于定义调度时间的元素,即按照什么时间规则去执行任务。Quartz 中主要提供了四种类型的 trigger:SimpleTrigger,CronTirgger,DateIntervalTrigger,和 NthIncludedDayTrigger。这四种 trigger 可以满足企业应用中的绝大部分需求。
在 Quartz 中,job 用于表示被调度的任务。主要有两种类型的 job:无状态的(stateless)和有状态的(stateful)。对于同一个 trigger 来说,有状态的 job 不能被并行执行,只有上一次触发的任务被执行完之后,才能触发下一次执行。Job 主要有两种属性:volatility 和 durability,其中 volatility 表示任务是否被持久化到数据库存储,而 durability 表示在没有 trigger 关联的时候任务是否被保留。两者都是在值为 true 的时候任务被持久化或保留。一个 job 可以被多个 trigger 关联,但是一个 trigger 只能关联一个 job。
在 Quartz 中, scheduler 由 scheduler 工厂创建:DirectSchedulerFactory 或者 StdSchedulerFactory。 第二种工厂 StdSchedulerFactory 使用较多,因为 DirectSchedulerFactory 使用起来不够方便,需要作许多详细的手工编码设置。 Scheduler 主要有三种:RemoteMBeanScheduler, RemoteScheduler 和 StdScheduler。本文以最常用的 StdScheduler 为例讲解。这也是笔者在项目中所使用的 scheduler 类。
线程图:
Scheduler 调度线程主要有两个: 执行常规调度的线程,和执行 misfired trigger 的线程。常规调度线程轮询存储的所有 trigger,如果有需要触发的 trigger,即到达了下一次触发的时间,则从任务执行线程池获取一个空闲线程,执行与该 trigger 关联的任务。Misfire 线程是扫描所有的 trigger,查看是否有 misfired trigger,如果有的话根据 misfire 的策略分别处理。
(四)启动流程:
若quartz是配置在spring中,当服务器启动时,就会装载相关的bean。SchedulerFactoryBean实现了InitializingBean接口,因此在初始化bean的时候,会执行afterPropertiesSet方法,该方法将会调用SchedulerFactory(DirectSchedulerFactory 或者 StdSchedulerFactory,通常用StdSchedulerFactory)创建Scheduler。SchedulerFactory在创建quartzScheduler的过程中,将会读取配置参数,初始化各个组件。
四、简单示例:
1 public class HelloJob implements Job { 2 public HelloJob() { 3 } 4 5 public void execute(JobExecutionContext context) throws JobExecutionException { 6 System.out.println("Hello World! - " + new Date()); 7 } 8 }
1 public class SimpleExample { 2 public void run() throws Exception { 3 System.out.println("------- 初始化 ----------------------"); 4 // 首先要实例化scheduler 5 SchedulerFactory schedulerFactory = new StdSchedulerFactory(); 6 Scheduler scheduler = schedulerFactory.getScheduler(); 7 System.out.println("------- 初始化完成 -----------"); 8 9 // 获取给定时间的下一个完整分钟的时间,例如给定时间 08:13:54 则会反回 08:14:00 10 Date runTime = DateBuilder.evenMinuteDate(new Date()); 11 12 System.out.println("------- Job安排 -------------------"); 13 // 获取job实例 14 JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build(); 15 // 在下一轮分钟触发运行 16 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").startAt(runTime).build(); 17 // 告诉quartz使用某个trigger执行某个job 18 scheduler.scheduleJob(job, trigger); 19 System.out.println(job.getKey() + " 将会运行于: " + runTime); 20 21 // 启动scheduler 22 scheduler.start(); 23 24 System.out.println("------- 开始安排 -----------------"); 25 System.out.println("------- 等待65秒 -------------"); 26 Thread.sleep(65L * 1000L); 27 28 // 关闭scheduler 29 System.out.println("------- 关闭 ---------------------"); 30 scheduler.shutdown(true); 31 System.out.println("------- 关闭完成 -----------------"); 32 } 33 34 public static void main(String[] args) throws Exception { 35 SimpleExample example = new SimpleExample(); 36 example.run(); 37 } 38 }
五、Spring集成:
1、新建spring工程以及maven引入quartz包;
2、application.xml:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" 4 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 5 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 7 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd 8 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 9 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd"> 10 11 <!-- 12 Spring整合Quartz进行配置遵循下面的步骤: 13 1:定义工作任务的Job 14 2:定义触发器Trigger,并将触发器与工作任务绑定 15 3:定义调度器,并将Trigger注册到Scheduler 16 --> 17 <!-- 1:定义任务的bean ,这里使用JobDetailFactoryBean,也可以使用MethodInvokingJobDetailFactoryBean ,配置类似--> 18 <bean name="hwJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> 19 <!-- 指定job的名称 --> 20 <property name="name" value="hw_job"/> 21 <!-- 指定job的分组 --> 22 <property name="group" value="hw_group"/> 23 <!-- 指定具体的job类 --> 24 <property name="jobClass" value="com.dufy.spring.quartz.chapter01.job.HelloWorldJob"/> 25 <!-- 必须设置为true,如果为false,当没有活动的触发器与之关联时会在调度器中会删除该任务 --> 26 <property name="durability" value="true"/> 27 <!-- 指定spring容器的key,如果不设定在job中的jobmap中是获取不到spring容器的 --> 28 <property name="applicationContextJobDataKey" value="applicationContext"/> 29 </bean> 30 <!-- 2.1:定义触发器的bean,定义一个Simple的Trigger,一个触发器只能和一个任务进行绑定 --> 31 <!-- <bean name="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> 32 指定Trigger的名称 33 <property name="name" value="hw_trigger"/> 34 指定Trigger的名称 35 <property name="group" value="hw_trigger_group"/> 36 指定Tirgger绑定的Job 37 <property name="jobDetail" ref="hwJob"/> 38 指定Trigger的延迟时间 1s后运行 39 <property name="startDelay" value="1000"/> 40 指定Trigger的重复间隔 5s 41 <property name="repeatInterval" value="5000"/> 42 指定Trigger的重复次数 43 <property name="repeatCount" value="5"/> 44 </bean> --> 45 <!-- 2.2:定义触发器的bean,定义一个Cron的Trigger,一个触发器只能和一个任务进行绑定 --> 46 <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> 47 <!-- 指定Trigger的名称 --> 48 <property name="name" value="hw_trigger"/> 49 <!-- 指定Trigger的名称 --> 50 <property name="group" value="hw_trigger_group"/> 51 <!-- 指定Tirgger绑定的Job --> 52 <property name="jobDetail" ref="hwJob"/> 53 <!-- 指定Cron 的表达式 ,当前是每隔1s运行一次 --> 54 <property name="cronExpression" value="0/1 * * * * ?" /> 55 </bean> 56 <!-- 3.定义调度器,并将Trigger注册到调度器中 --> 57 <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> 58 <property name="triggers"> 59 <list> 60 <!-- <ref bean="simpleTrigger"/> --> 61 <ref bean="cronTrigger"/> 62 </list> 63 </property> 64 <!-- <property name="autoStartup" value="true" /> --> 65 </bean> 66 <!-- 持久化数据配置,需要添加quartz.properties--> 67 <bean name="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> 68 <property name="applicationContextSchedulerContextKey" value="applicationContextKey"/> 69 <property name="configLocation" value="classpath:quartz.properties"/> 70 </bean> 71 </beans>
3、quartz.properties:
1 org.quartz.scheduler.instanceName: dufy_test 2 org.quartz.scheduler.instanceId = AUTO 3 4 org.quartz.scheduler.rmi.export: false 5 org.quartz.scheduler.rmi.proxy: false 6 org.quartz.scheduler.wrapJobExecutionInUserTransaction: false 7 #============================================================================ 8 # Configure ThreadPool 9 #============================================================================ 10 org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool 11 org.quartz.threadPool.threadCount: 2 12 org.quartz.threadPool.threadPriority: 5 13 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true 14 15 org.quartz.jobStore.misfireThreshold: 60000 16 #============================================================================ 17 # Configure JobStore 18 #============================================================================ 19 20 #default config 21 #org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore 22 #持久化配置 23 org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX 24 org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate 25 org.quartz.jobStore.useProperties:true 26 27 #============================================================================ 28 #havent cluster spring 29 #============================================================================ 30 org.quartz.jobStore.isClustered = false 31 32 #数据库表前缀 33 org.quartz.jobStore.tablePrefix:qrtz_ 34 org.quartz.jobStore.dataSource:qzDS 35 36 #============================================================================ 37 # Configure Datasources 38 #============================================================================ 39 #JDBC驱动 40 org.quartz.dataSource.qzDS.driver:com.mysql.jdbc.Driver 41 org.quartz.dataSource.qzDS.URL:jdbc:mysql://localhost:3306/quartz_test 42 org.quartz.dataSource.qzDS.user:root 43 org.quartz.dataSource.qzDS.password:root 44 org.quartz.dataSource.qzDS.maxConnection:10
4、Job类的实现:
1 public class HelloWorldJob implements Job{ 2 3 private Logger log = LoggerFactory.getLogger(this.getClass()); 4 5 public void execute(JobExecutionContext arg0) throws JobExecutionException { 6 log.info("This is a first spring combine quartz !"); 7 log.info("Welcome to Spring_Quartz World!"+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) ); 8 log.info("Let's begin ! \n \n"); 9 } 10 }
5、调度程序的实现:
1 public class HWTest { 2 private static Scheduler scheduler; 3 4 public static void main(String[] args) { 5 ApplicationContext ac = new ClassPathXmlApplicationContext("spring_quartz.xml"); 6 scheduler = (StdScheduler)ac.getBean("scheduler"); 7 startSchedule(); 8 } 9 10 public static void startSchedule() { 11 try { 12 // 1、创建一个JobDetail实例,指定Quartz 13 JobDetail jobDetail = JobBuilder.newJob(HelloWorldJob.class) 14 .withIdentity("job1_1", "jGroup1") 15 // 任务名,任务组 16 .build(); 17 // 触发器类型 18 //SimpleScheduleBuilder builder = SimpleScheduleBuilder 19 // 设置执行次数 20 //.repeatSecondlyForTotalCount(5); 21 CronScheduleBuilder builder = CronScheduleBuilder.cronSchedule("0/2 * * * * ?"); 22 // 2、创建Trigger 23 Trigger trigger = TriggerBuilder.newTrigger() 24 .withIdentity("trigger1_1", "tGroup1").startNow() 25 .withSchedule(builder) 26 .build(); 27 // 3、创建Scheduler 28 scheduler = StdSchedulerFactory.getDefaultScheduler(); 29 scheduler.start(); 30 // 4、调度执行 31 scheduler.scheduleJob(jobDetail, trigger); 32 } catch (SchedulerException e) { 33 e.printStackTrace(); 34 } 35 } 36 }
六、参考资料:
https://www.cnblogs.com/drift-ice/p/3817269.html
https://blog.csdn.net/lkl_csdn/article/details/73613033
https://blog.csdn.net/u010648555/article/details/53363321
https://blog.csdn.net/gjb724332682/article/details/53019953?utm_source=blogkpcl6