大家好,今天给大家分享一下:Quartz框架的介绍。
虽然上个项目中并没有明确要求使用Quartz框架,但是涉及一个SpringRMI中web\service的分离,其中要求在一台service宕机之后,web就不连接它,直接忽略它。而RMI是原生的Spring框架,所以当时没有找到负载均衡这方面的功能,于是就想到了使用定时任务来发送生存报告到缓存中,然后去缓存中查看是否存在对应的生存报告再决定要不要使用那台service。
而定时任务,第一选择就是Quartz框架了。
一、背景介绍
1.Quartz可以做什么
Quartz是一个任务调度框架。你可以将其理解为一个自定义闹钟,我们平时生活中很多东西都离不开定时功能,比如你遇到这样的问题
- 想每月25号,信用卡自动还款
- 想每天早上7点,中午11.30,晚上6.20命令电饭锅自动烧饭
- 想在电脑开机时,每隔1小时,备份一下自己的爱情动作片,学习笔记到云盘
- 想每隔0.5秒确认一下抢票有没有开始
这些问题总结起来就是:在某一个有规律的时间点干某件事。并且时间的触发的条件可以非常复杂(比如每月最后一个工作日的17:50),复杂到需要一个专门的框架来干这个事。 Quartz就是来干这样的事,你给它一个触发条件的定义,它负责到了时间点,触发相应的Job起来干活。
二、知识剖析
1.Quartz的组成
1)Scheduler调度器:所有的调度都是由它控制。
2)Trigger触发器:定义触发的条件,主要涉及定义时间条件。
通用属性(有但是不一定用得到):
StartTime&EndTime:顾名思义,开始和结束时间,date类型
Priority:优先级,当多个任务执行,系统资源不够时,按照优先级来选择执行,默认为5,越高优先级越高,负数也是5.
Misfire:错失触发策略,一般是资源不足或是重启等问题导致某些任务没有触发或者触发少了的应对策略,有很多选择,包括一次触发Miss的,忽略Miss的等等,看情况选择。
Calinder:补充Trigger中时间设计不足的点。可以指定特定时间,或者是时间段等。
触发器其分为四种
a.SimpleTrigger:顾名思义,最简单的触发器,指定时间开始,以一定时间间隔(毫秒)执行任务。适合间隔时间短时间多次重复的任务
属性:
repeatInterval 重复间隔
repeatCount 重复次数。实际执行次数是 repeatCount+1。因为在startTime的时候一定会执行一次。
b.CanlinderIntervalTrigger:类似于SimpleTrigger,指定从某一个时间开始,以一定的时间间隔执行的任务。 但是不同的是SimpleTrigger指定的时间间隔为毫秒,没办法指定每隔一个月执行一次(每月的时间间隔不是固定值),而CalendarIntervalTrigger支持的间隔单位有秒,分钟,小时,天,月,年,星期。
相较于SimpleTrigger有两个优势:1、更方便,比如每隔1小时执行,你不用自己去计算1小时等于多少毫秒。 2、支持不是固定长度的间隔,比如间隔为月和年。但劣势是精度只能到秒。
它适合的任务类似于:9:00 开始执行,并且以后每周 9:00 执行一次
属性:
interval 执行间隔
intervalUnit 执行间隔的单位(秒,分钟,小时,天,月,年,星期)
c.DailyTimeIntervalTrigger
指定每天的某个时间段内,以一定的时间间隔执行任务。并且它可以支持指定星期。
它适合的任务类似于:指定每天9:00 至 18:00 ,每隔70秒执行一次,并且只要周一至周五执行。
属性:
startTimeOfDay 每天开始时间
endTimeOfDay 每天结束时间
daysOfWeek 需要执行的星期
interval 执行间隔
intervalUnit 执行间隔的单位(秒,分钟,小时,天,月,年,星期)repeatCount 重复次数
d.CronTrigger
适合于更复杂的任务,它支持类型于Linux Cron的语法(并且更强大)。基本上它覆盖了以上三个Trigger的绝大部分能力(但不是全部)—— 当然,也更难理解。
它适合的任务类似于:每天0:00,9:00,18:00各执行一次。
属性:
cron表达式,类似正则表达式,需要用的时候学习一下,针对自己需要的场景仿照着写一个就可以,没必要深究。
举个例子:
0 15 10 ? * 6L 代表什么意思?
每月最后一个星期五10:15分运行。
3)JobDetail任务细节定义器:
具体干下面几件事:
a.创建具体任务
b.定义一个JobDetail引用这个任务
c.将其加入ScheduleJob
三、常见问题
讲了那么多,看起来好像很复杂,那么到底怎么在spring中来一站傻瓜式实现定时器的功能呢?
毕竟我们有时候只是单纯用一下定时功能,并不想去深入研究。
四、解决方案
1.首先需要导入相关依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>4.3.3.RELEASE</spring.version>
<slf4j.version>1.7.12</slf4j.version>
<log4j.version>1.2.17</log4j.version>
</properties>
<dependencies>
<!--日志管理包-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!--spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- 作业任务调度机制 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
</dependencies>
2.编写需要执行的定时任务的代码
//定时任务
public class QuartzTask {
String name="java";
// 任务方法名
public void doTask(){
//定义任务内容为输出一段字符
System.out.println("学习"+name+"头冷");
}
}
3.在applicationContext.xml中创建调度器,触发器,任务细节
可以看到我这里就是使用的SimpleTrigger,可以设置重复时间间隔和次数。
<!-- Quartz -->
<!--自定义任务类-->
<bean name="quartzTask" class="com.demo.QuartzTask" />
<!--JobDetail-->
<bean id="quartzTaskJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<!--目标类,引用目标类-->
<property name="targetObject" ref="quartzTask"/>
<!--使用目标类中的哪个方法-->
<property name="targetMethod" value="doTask"/>
<!--是否同时进行,默认为true-->
<property name="concurrent" value="false"/>
</bean>
<!--Trigger,自定义触发器,定义触发时间-->
<bean id="quartzTaskTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<!--具体任务细节,引用上面的自定义任务-->
<property name="jobDetail" ref="quartzTaskJob"/>
<!--设定开始时间,date类型-->
<!--<property name="startTime" value="2018-06-12-18:00"/>-->
<!-- 程序开始后延迟几秒启动定时任务,单位ms -->
<property name="startDelay" value="2000" />
<!-- 每隔多久重复一次 ,单位ms-->
<property name="repeatInterval" value="500" />
<!--设定重复次数,不设定默认为-1,代表一直重复,实际执行次数为repeatCount+1-->
<!--<property name="repeatCount" value="-1"/>-->
<!--设定优先级,当多个同时段的任务一起触发,系统资源又不足时,优先级越高越优先触发,默认为5,如果负数就设置为默认值-->
<!--<property name="priority" value="5"/>-->
</bean>
<!--Scheduler,创建调度工厂-->
<bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<!--引用上面的自定义触发器-->
<ref local="quartzTaskTrigger" />
</list>
</property>
</bean>
4.加载配置,启动即可
public class QuartzMain {
public static void main(String[] args) {
//加载配置文件
new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("定时任务启动");
}
}
5.启动结果如下
可以看到定时任务启动成功了,可以针对自己的需要来调整相关设置。
五、更多讨论
1.如果不用Quartz应该怎么做?
其实我本身没接触过其他的定时相关的设置,所以见识比较浅薄。在我看来如果不使用这个框架还想使用定时功能,可能就得使用while循环,循环条件设置为需要的时间,然后将线程阻塞一下,比如设置为一秒循环一次。这样也可以实现定时功能。只不过就不能像使用Quartz那样有更多设置罢了。
2.前面的几个例子我们应该各自用哪个触发器呢?
- 想每月25号,信用卡自动还款——可以使用CanlinderIntervalTrigger
- 想每天早上7点,中午11.30,晚上6.20命令电饭锅自动烧饭——可以使用CornTrigger
- 想在电脑开机时,每隔1小时,备份一下自己的爱情动作片,学习笔记到云盘——可以使用DailyTimeIntervalTrigger
- 想每隔0.5秒确认一下抢票有没有开始——可以使用SimpleTrigger
3.我用了Quartz是不是就可以高枕无忧了?
答案显然是不能。因为前面就说了在系统高并发超过连接池数量或者系统内存不够的情况下,其会优先安排优先级高的程序,所以如果你的程序优先级比较低就有可能产生Miss的情况,这也是为什么要使用MisFire策略。1
六、参考文献
https://www.cnblogs.com/drift-ice/p/3817269.html