开发需求中,我们经常会遇到定时处理的需求,比如说定时提醒用户还款、定时备份数据库、定时去合作公司文件服务器读取数据、产品自动下架、用户所下的订单超时取消……等等等等,从本篇文章开始,我们来一起讨论一下项目中定时任务的应用。
然而我们都知道:是先有业务,再有技术。技术是为业务而生的。所以对于不同的需求,我们需要使用不同的技术,来合理的解决需求的问题。总结一下,通过以下四个技术层面,能够解决几乎所有的定时需求:
1、Spring实现的内存级quartz;
2、JDK中Timer及JDK中其他定时处理类;
3、集成Spring,将quartz持久化到mongodb中;
4、quartz集群。
……
今天,我们来讲第一个,也是最简单的一个。
一、Spring实现的内存级quartz
该定时任务,主要是Spring对quartz框架做的一个实现。它只需要进行简单的配置,即可实现定时任务。配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core.xsd"
default-lazy-init="true">
<!--注入一个bean -->
<bean id="testQtz" class="com.quartz.TestQtz" />
<!--指定定时执行的方法 -->
<bean id="testQtzJobMethod"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject">
<ref bean="testQtz" />
</property>
<property name="targetMethod">
<value>executeQtz</value> <!--指定testQtz Bean中,定时执行的方法名称 -->
</property>
</bean>
<!-- ======================== 调度触发器 ======================== -->
<!-- 设置几点开始周期性 的 执行 -->
<bean id="testQtzCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="testQtzJobMethod" />
<property name="cronExpression" value="0 01 0 * * ?" /> <!-- 工作日的 00:01分执行 -->
</bean>
<!-- ======================== 调度工厂 ======================== -->
<!-- 调度工厂 -->
<bean id="SpringJobSchedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref local="testQtzCronTrigger" />
</list>
</property>
</bean>
</beans>
定时任务将执行的方法类如下:
package com.quartz.testQtz;
public class TestQtz {
private static int counter = 0;
public void executeQtz() {
counter++;
System.out.println("第 " + counter +" 次执行");
}
}
这样就可以了,上面的配置加方法,就可以执行定时任务了,很简单吧。
下面我们来大概说一下它的原理。
上面的这种定时任务的实现,还是非常普遍的,因为使用起来非常的简单,只需要一个Spring的简单的配置,即可完成需求。实际上,这是Spring做的一个对Quartz的一个比较完整的实现,所以我们用来起来才没有感觉到Quartz的存在,所以才会觉得很简单。
我们通过上面Spring的配置,通过设置cronExpression,我们可以使要执行的方法周期性的执行。比如每天晚上12点钟,准时备份数据库,比如还差3天用户将逾期时,发送提醒短信等等。我们都可以使用该种方式执行。
尺有所长,寸有所短。尽管上述方法用起来很方便,但是我能够容纳它的缺点,才能够对症下药。
缺点如下:
1、该种方法,是将定时任务序列化到内存当中的。也就是说,当系统重新部署,需要重启服务器时,该定时任务会从内存中清除(几乎是废话,应用服务器都停了,定时任务还怎么能够在内存当中。。),当应用服务器重新启动后,定时任务才会重新载入内存当中。
所以说,这种定时任务,应用服务器停止时间如果比较长的话,则当日此期间未执行的定时任务,就没有机会执行了。
2、执行执行并不是非常准确的(即执行时间不准确)。也就是说定点儿执行的定时任务,到了时间可能仍然没有执行。这是为什么呢?定时任务,说白了,就是一个进程。应用服务器启动时,定时任务进入内存,并进入阻塞状态。这时,我们可以理解为有一个timer不断去扫面执行时间。当到执行时间是,经过内存调度,即将执行的定时任务进入就绪状态,等待CPU进行调度,注意它并不会阻塞其他线程的调度,而是将自己置于就绪状态等待CPU空闲时调度。所以说,如果CPU一直忙碌的话,就会发生定时任务到时间仍未执行的情况。
综合上述这两种缺点,我们就清楚了如何、以及合适使用该种定时任务了。
实际应用中,由于这种定时任务的需求比较特殊,我们经常会将定时任务分离出来,将其作为一个独立的项目,独立部署。因为应用服务器是需要更改的,不断的产品迭代、产品的上线,很可能会重新启动应用服务器,为了不对定时任务造成影响,我们通常会将其分离出来对部署。
由于定时任务,并非准确执行,我们需要容忍它这一点(其实相差时间不会很大)。
本文只是简单介绍了定时任务,非常简单的一种实现,接下来我将会介绍quartz强大的功能,敬请期待。