定時任務知多少(一)——Spring實現記憶體級quartz
開發需求中,我們經常會遇到定時處理的需求,比如說定時提醒使用者還款、定時備份資料庫、定時去合作公司檔案伺服器讀取資料、產品自動下架、使用者所下的訂單超時取消……等等等等,從本篇文章開始,我們來一起討論一下專案中定時任務的應用。
然而我們都知道:是先有業務,再有技術。技術是為業務而生的。所以對於不同的需求,我們需要使用不同的技術,來合理的解決需求的問題。總結一下,通過以下四個技術層面,能夠解決幾乎所有的定時需求:
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強大的功能,敬請期待。