什么是Quartz? Quartz是一款基于Java语言的作业调度框架。一般用来创建或简单或复杂的调度时间表,执行Java下任意数量的作业。
可以通过CronTrigger定义Quartz的调度时间表(例如0 0 12 ? * WED
表示“每周三上午12:00”)。此外,时间表也可以通过SimpleTrigger,由Date定义触发的开始时间、毫秒的时间间隔和重复计数(例如“在下周三12:00,然后每隔10秒、执行5次”)。可以使用Calender定义例外的日程(例如“没有周末和节假日”)。
作业可以是实现了Job接口任意的Java类。作业监听器(JobListener)和触发器监听器(TriggerListener)通知作业的执行(和其他事件)。作业及其触发器可以被持久化。Quartz一般用于企业级应用程序,以支持工作流、系统管理(维护)活动,并在应用程序中提供实时的服务。Quartz还支持集群。
Quartz基本元素 从上面的介绍可以看到,Quartz主要由三个部分组成,分别是调度器,作业详情以及触发器。
调度器 调度器(Scheduler)的作用主要是通过作业详情(JobDetail)和触发器(Trigger)来执行特定的任务。调度器的生命周期从它的start()
开始,一直到shutdown()
方法结束。在使用调度器的时候,必须先执行start()
方法,才能够开始任务的调度。当然了,在Spring当中,已经帮我们封装好了,所以直接声明一个调度器类就好了。
作业详情 先说说作业(Job)吧,作业就是我们调度器需要执行的具体任务逻辑。在实际的代码中就是指实现了org.quartz.Job
接口的任何类。orf.quartz.Job
接口只有一个方法void execute(JobExecutionContext context)
,因此任务调度时候执行的就是execute
方法的逻辑。
那么作业详情(JobDetail)又是什么呢?作业详情其实是对作业的一个封装,会指定它的名称(name)和组别(group)。这两个属性非常重要,因为这两个属性是这个作业详情在整个调度系统中的唯一编号。我们可以通过作业详情向作业中传入一些数据(JobDataMap),在运行时能通过execute
方法的context
参数获取到。context
就是上下文参数,用于获取作业详情传递过来的数据。
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 JobDetail job = newJob(DumbJob.class) .withIdentity("myJob" , "group1" ) .usingJobData("jobSays" , "Hello World!" ) .usingJobData("myFloatValue" , 3.141f ) .build(); public class DumbJob implements Job { public DumbJob () { } public void execute (JobExecutionContext context) throws JobExecutionException { JobKey key = context.getJobDetail().getKey(); JobDataMap dataMap = context.getJobDetail().getJobDataMap(); String jobSays = dataMap.getString("jobSays" ); float myFloatValue = dataMap.getFloat("myFloatValue" ); System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue); } }
触发器 触发器(Trigger)是什么呢?其实在传统的Java调度系统中,我们直接在调度器中指定执行时间,执行频率就可以了。但是在Quartz中,开发者将调度器的执行和触发两个步骤解耦了。这样做的好处是可以更加灵活地配置各种需求的任务。所以,触发器只是一个引起任务执行的条件。
触发器主要用于指定任务执行的时间,频率以及重复次数等等。
1 2 3 4 5 6 7 8 9 10 11 12 HolidayCalendar cal = new HolidayCalendar(); cal.addExcludedDate( someDate ); cal.addExcludedDate( someOtherDate ); sched.addCalendar("myHolidays" , cal, false ); Trigger t = newTrigger() .withIdentity("myTrigger" ) .forJob("myJob" ) .withSchedule(dailyAtHourAndMinute(9 , 30 )) .modifiedByCalendar("myHolidays" ) .build();
Quartz使用方式 下面给出一个Quartz的使用例子,纯Java客户端实现。
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 package com.qinjiangbo.scheduler;import org.quartz.JobDetail;import org.quartz.Scheduler;import org.quartz.SchedulerException;import org.quartz.Trigger;import org.quartz.impl.StdSchedulerFactory;import static org.quartz.JobBuilder.newJob;import static org.quartz.SimpleScheduleBuilder.simpleSchedule;import static org.quartz.TriggerBuilder.newTrigger;public class ProxyDumpJob { public static void main (String[] args) { try { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.start(); JobDetail jobDetail = newJob(HelloJob.class) .withIdentity("firstJob" , "qinjiangbo" ) .build(); Trigger trigger = newTrigger() .withIdentity("firstTrigger" , "qinjiangbo" ) .startNow() .withSchedule(simpleSchedule() .withIntervalInSeconds(5 ) .repeatForever()) .build(); scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException e) { e.printStackTrace(); } } }
HelloJob.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.qinjiangbo.scheduler;import org.quartz.Job;import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;import org.quartz.JobKey;import java.util.Date;public class HelloJob implements Job { @Override public void execute (JobExecutionContext context) throws JobExecutionException { System.out.println(new Date().toString()); } }
与Spring集成 和Spring的集成和这个过程比较相似,只不过作业详情和触发器的实例化都是通过Spring的Bean工厂实现的了。
maven配置 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 <dependency > <groupId > org.quartz-scheduler</groupId > <artifactId > quartz</artifactId > <version > 2.3.0</version > </dependency > <dependency > <groupId > org.quartz-scheduler</groupId > <artifactId > quartz-jobs</artifactId > <version > 2.3.0</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 4.3.12.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context-support</artifactId > <version > 4.3.12.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-web</artifactId > <version > 4.3.12.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > 4.3.12.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-tx</artifactId > <version > 4.3.12.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-core</artifactId > <version > 4.3.12.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-orm</artifactId > <version > 4.3.12.RELEASE</version > </dependency >
Quartz的Spring配置 创建spring-quartz.xml
文件,如下:
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 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="proxyDumpJob" class ="org.springframework.scheduling.quartz.JobDetailFactoryBean" > <property name ="name" value ="firstJob" /> <property name ="group" value ="acs" /> <property name ="durability" value ="true" /> <property name ="jobClass" value ="com.qinjiangbo.scheduler.AcsProxyDumpJob" /> </bean > <bean id ="proxyDumpTrigger" class ="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean" > <property name ="name" value ="firstTrigger" /> <property name ="group" value ="acs" /> <property name ="startTime" ref ="now" /> <property name ="repeatInterval" value ="5000" /> <property name ="jobDetail" ref ="proxyDumpJob" /> </bean > <bean id ="now" class ="java.util.Date" /> <bean class ="org.springframework.scheduling.quartz.SchedulerFactoryBean" > <property name ="jobDetails" > <list > <ref bean ="proxyDumpJob" /> </list > </property > <property name ="triggers" > <list > <ref bean ="proxyDumpTrigger" /> </list > </property > </bean > </beans >
作业类 AcsProxyDumpJob.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.qinjiangbo.scheduler;import org.quartz.Job;import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;import java.util.Date;public class AcsProxyDumpJob implements Job { @Override public void execute (JobExecutionContext context) throws JobExecutionException { System.out.println(new Date().toString()); } }
任务执行情况 启动tomcat服务器,可以看到控制台的情况如下:
1 2 3 4 5 6 7 8 9 10 11 ... 2017-12-08 13:40:55 [INFO] - org.springframework.scheduling.quartz.SchedulerFactoryBean - SchedulerFactoryBean.java(645) - Starting Quartz Scheduler now 2017-12-08 13:40:55 [INFO] - org.quartz.core.QuartzScheduler - QuartzScheduler.java(547) - Scheduler org.springframework.scheduling.quartz.SchedulerFactoryBean#0_$_NON_CLUSTERED started. Fri Dec 08 13:40:55 CST 2017 2017-12-08 13:40:55 [INFO] - org.springframework.web.servlet.DispatcherServlet - FrameworkServlet.java(508) - FrameworkServlet 'servlet': initialization completed in 9163 ms 08-Dec-2017 13:40:57.192 INFO [http-nio-8080-exec-2] org.apache.tomcat.util.http.parser.Cookie.logInvalidHeader A cookie header was received [1512543852,1512550588,1512666728; Hm_lpvt_a0e256d0086924d62a9116fdbab18bdf=1512668909; __cfduid=dcd329b1d40b3086b0603537f1e8222d81512702662] that contained an invalid cookie. That cookie will be ignored.Note: further occurrences of this error will be logged at DEBUG level. Fri Dec 08 13:41:00 CST 2017 Fri Dec 08 13:41:05 CST 2017 Fri Dec 08 13:41:10 CST 2017 ... ...
可以看到项目成功启动,任务也按照预先设定的成功执行。
总结 Quartz作为一款非常优秀的调度框架提供了非常丰富的功能。但是也存在一些先天的不足,比如没有直接提供监控台,而阿里的SchedulerX做的比这个要好,目前已经在阿里云上推出了相关的产品,感兴趣的可以去了解一下。关于Quartz的系统架构以及Quartz和Spring Scheduling以及Java的调度系统等等这些调度器之间的区别,我会在后面的文章中陆续写到,欢迎大家持续关注。
参考资料
:https://en.wikipedia.org/wiki/Quartz_(scheduler)