Spring 定時(shí)任務(wù)

個(gè)人專題目錄


一藕各、實(shí)現(xiàn)方法

spring中實(shí)現(xiàn)的定時(shí)任務(wù),大致有四種方法:

  • web接口片效,使用crontab調(diào)用
  • 使用@Scheduled注解
  • 使用Quartz
  • java類繼承TimerTask

這四類的比較如下:

方法 配置項(xiàng) 集群模式 使用場(chǎng)景
web接口 較少 集群模式下需要申請(qǐng)域名红伦,通過域名調(diào)用 web項(xiàng)目
@Scheduled注解 不支持集群模式,集群模式下每個(gè)節(jié)點(diǎn)都會(huì)調(diào)用注解標(biāo)示的任務(wù) 單節(jié)點(diǎn)項(xiàng)目
TimerTask 不支持集群模式淀衣,集群模式下每個(gè)節(jié)點(diǎn)都會(huì)調(diào)用注解標(biāo)示的任務(wù) 單節(jié)點(diǎn)項(xiàng)目
Quartz 需要額外數(shù)據(jù)庫來支持集群模式 分布式項(xiàng)目

下面詳細(xì)介紹下基于注解和quartz的使用方法昙读。

二、@Scheduled注解

2.1 依賴包

Scheduled依賴包

<dependency> 
        <groupId>org.springframework</groupId> 
        <artifactId>spring-context</artifactId> 
        <version>${spring.version}</version> 
</dependency>

2.2 配置文件

Schedule配置文件

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns:task="http://www.springframework.org/schema/task" 
    xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xsi:schemaLocation=" 
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-3.0.xsd 
http://www.springframework.org/schema/task 
http://www.springframework.org/schema/task/spring-task-3.1.xsd"> 
   
    <!-- 開啟定時(shí)任務(wù) --> 
    <task:annotation-driven />
    <!-- 配置線程池膨桥,若不配置所有的定時(shí)任務(wù)會(huì)串行執(zhí)行 --> 
        <task:annotation-driven scheduler="myScheduler"/>  
    <task:scheduler id="myScheduler" pool-size="5"/> 
   
    <!-- 開啟注解 --> 
    <context:annotation-config /> 
    <!-- 指定相關(guān)的包路徑 --> 
    <context:component-scan base-package="com.spring.task"/>   <!-- MyTask中使用注解 -->
  
    <!-- 當(dāng)然你也可以不在java類中使用注解蛮浑,此時(shí)需要如下配置 -->        
        <bean id="myTask" class="com.spring.task.MyTask2"></bean>  <!-- MyTask2中不使用注解,在配置文件配置執(zhí)行信息 --> 
    <task:scheduled-tasks>
      <!-- 這里表示的是每隔五秒執(zhí)行一次 -->
      <task:scheduled ref="myTask2" method="show" cron="*/5 * * * * ?" />
      <task:scheduled ref="myTask2" method="print" cron="*/10 * * * * ?"/>
    </task:scheduled-tasks>
</beans>

2.3 Job類實(shí)現(xiàn)

Scheduled任務(wù)類

package com.spring.task;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
  
/**
 * 基于注解的定時(shí)器任務(wù)
 */
@Component
public class MyTask {
  
  /**
   * 定時(shí)計(jì)算国撵。每天凌晨 01:00 執(zhí)行一次
   */
  @Scheduled(cron = "0 0 1 * * *")
  public void show() {
    System.out.println("show method 2");
  }
  
  /**
   * 啟動(dòng)時(shí)執(zhí)行一次陵吸,之后每隔2秒執(zhí)行一次
   */
  @Scheduled(fixedRate = 1000*2) 
  public void print() {
    System.out.println("print method 2");
  }
}
非注解任務(wù)  展開原碼
package com.spring.task;
  
/**
 * 不使用注解玻墅,自定義任務(wù)介牙,此時(shí)就是普通的java類
 */
public class MyTask2 {
  
  public void show() {
    System.out.println("show method 1");
  }
  
  public void print() {
    System.out.println("print method 1");
  }
}

三、Quartz

3.1 Quartz任務(wù)調(diào)度的基本實(shí)現(xiàn)原理

Quartz是OpenSymphony開源組織在任務(wù)調(diào)度領(lǐng)域的一個(gè)開源項(xiàng)目澳厢,完全基于Java實(shí)現(xiàn)环础。作為一個(gè)優(yōu)秀的開源調(diào)度框架,Quartz具有以下特點(diǎn):

(1)強(qiáng)大的調(diào)度功能剩拢,例如支持豐富多樣的調(diào)度方法稠炬,可以滿足各種常規(guī)及特殊需求诬滩;

(2)靈活的應(yīng)用方式,例如支持任務(wù)和調(diào)度的多種組合方式,支持調(diào)度數(shù)據(jù)的多種存儲(chǔ)方式莺掠;

(3)分布式和集群能力惯裕,Terracotta收購后在原來功能基礎(chǔ)上作了進(jìn)一步提升。本文將對(duì)該部分相加闡述。

3.1.1 Quartz 核心元素

Quartz任務(wù)調(diào)度的核心元素為:Scheduler——任務(wù)調(diào)度器祸穷、Trigger——觸發(fā)器、Job——任務(wù)勺三。其中trigger和job是任務(wù)調(diào)度的元數(shù)據(jù)雷滚,scheduler是實(shí)際執(zhí)行調(diào)度的控制器。

Trigger是用于定義調(diào)度時(shí)間的元素吗坚,即按照什么時(shí)間規(guī)則去執(zhí)行任務(wù)祈远。Quartz中主要提供了四種類型的trigger:SimpleTrigger,CronTirgger商源,DateIntervalTrigger车份,和NthIncludedDayTrigger。這四種trigger可以滿足企業(yè)應(yīng)用中的絕大部分需求炊汹。

Job用于表示被調(diào)度的任務(wù)躬充。主要有兩種類型的job:無狀態(tài)的(stateless)和有狀態(tài)的(stateful)。對(duì)于同一個(gè)trigger來說讨便,有狀態(tài)的job不能被并行執(zhí)行充甚,只有上一次觸發(fā)的任務(wù)被執(zhí)行完之后,才能觸發(fā)下一次執(zhí)行霸褒。Job主要有兩種屬性:requestsRecovery和durability伴找,其中requestsRecovery表示任務(wù)是否在發(fā)生故障的時(shí)候在其他節(jié)點(diǎn)執(zhí)行,而durability表示在沒有trigger關(guān)聯(lián)的時(shí)候任務(wù)是否被保留废菱。兩者都是在值為true的時(shí)候任務(wù)被持久化或保留技矮。一個(gè)job可以被多個(gè)trigger關(guān)聯(lián),但是一個(gè)trigger只能關(guān)聯(lián)一個(gè)job殊轴。

Scheduler由scheduler工廠創(chuàng)建:DirectSchedulerFactory或者StdSchedulerFactory衰倦。第二種工廠StdSchedulerFactory使用較多,因?yàn)镈irectSchedulerFactory使用起來不夠方便旁理,需要作許多詳細(xì)的手工編碼設(shè)置樊零。Scheduler主要有三種:RemoteMBeanScheduler,RemoteScheduler和StdScheduler孽文。

Quartz核心元素之間的關(guān)系如圖所示:

01.png
                                     核心元素關(guān)系圖

3.1.2 Quartz 線程視圖

在Quartz中驻襟,有兩類線程,Scheduler調(diào)度線程和任務(wù)執(zhí)行線程芋哭,其中任務(wù)執(zhí)行線程通常使用一個(gè)線程池維護(hù)一組線程沉衣。

02.png
                                    Quartz線程視圖

Scheduler調(diào)度線程主要有兩個(gè):執(zhí)行常規(guī)調(diào)度的線程,和執(zhí)行misfiredtrigger的線程减牺。常規(guī)調(diào)度線程輪詢存儲(chǔ)的所有trigger豌习,如果有需要觸發(fā)的trigger存谎,即到達(dá)了下一次觸發(fā)的時(shí)間,則從任務(wù)執(zhí)行線程池獲取一個(gè)空閑線程肥隆,執(zhí)行與該trigger關(guān)聯(lián)的任務(wù)愕贡。Misfire線程是掃描所有的trigger,查看是否有misfiredtrigger巷屿,如果有的話根據(jù)misfire的策略分別處理(fire now OR wait for the next fire)固以。

3.1.3 Quartz Job數(shù)據(jù)存儲(chǔ)

Quartz中的trigger和job需要存儲(chǔ)下來才能被使用。Quartz中有兩種存儲(chǔ)方式:RAMJobStore,JobStoreSupport嘱巾,其中RAMJobStore是將trigger和job存儲(chǔ)在內(nèi)存中憨琳,而JobStoreSupport是基于jdbc將trigger和job存儲(chǔ)到數(shù)據(jù)庫中。RAMJobStore的存取速度非逞眩快篙螟,但是由于其在系統(tǒng)被停止后所有的數(shù)據(jù)都會(huì)丟失,所以在集群應(yīng)用中问拘,必須使用JobStoreSupport遍略。

03.png

3.2 Quartz集群原理

3.2.1 Quartz 集群架構(gòu)

一個(gè)Quartz集群中的每個(gè)節(jié)點(diǎn)是一個(gè)獨(dú)立的Quartz應(yīng)用,它又管理著其他的節(jié)點(diǎn)骤坐。這就意味著你必須對(duì)每個(gè)節(jié)點(diǎn)分別啟動(dòng)或停止绪杏。Quartz集群中,獨(dú)立的Quartz節(jié)點(diǎn)并不與另一其的節(jié)點(diǎn)或是管理節(jié)點(diǎn)通信纽绍,而是通過相同的數(shù)據(jù)庫表來感知到另一Quartz應(yīng)用的蕾久,如圖所示。

04.png
                                    Quartz集群架構(gòu)

3.2.2 Quartz集群相關(guān)數(shù)據(jù)庫表

因?yàn)镼uartz集群依賴于數(shù)據(jù)庫拌夏,所以必須首先創(chuàng)建Quartz數(shù)據(jù)庫表僧著,Quartz發(fā)布包中包括了所有被支持的數(shù)據(jù)庫平臺(tái)的SQL腳本。這些SQL腳本存放于<quartz_home>/docs/dbTables 目錄下障簿。這些表的簡(jiǎn)要介紹如和創(chuàng)建語句如下盹愚。

數(shù)據(jù)表說明

# 數(shù)據(jù)庫所需表
# QRTZ_CALENDARS 以 Blob 類型存儲(chǔ) Quartz 的 Calendar 信息
# QRTZ_CRON_TRIGGERS 存儲(chǔ) Cron Trigger,包括Cron表達(dá)式和時(shí)區(qū)信息
# QRTZ_FIRED_TRIGGERS 存儲(chǔ)與已觸發(fā)的 Trigger 相關(guān)的狀態(tài)信息站故,以及相聯(lián) Job的執(zhí)行信息QRTZ_PAUSED_TRIGGER_GRPS 存儲(chǔ)已暫停的 Trigger組的信息
# QRTZ_SCHEDULER_STATE 存儲(chǔ)少量的有關(guān) Scheduler 的狀態(tài)信息皆怕,和別的Scheduler實(shí)例(假如是用于一個(gè)集群中)
# QRTZ_LOCKS 存儲(chǔ)程序的悲觀鎖的信息(假如使用了悲觀鎖)
# QRTZ_JOB_DETAILS 存儲(chǔ)每一個(gè)已配置的 Job 的詳細(xì)信息
# QRTZ_JOB_LISTENERS 存儲(chǔ)有關(guān)已配置的 JobListener的信息
# QRTZ_SIMPLE_TRIGGERS存儲(chǔ)簡(jiǎn)單的Trigger,包括重復(fù)次數(shù)世蔗,間隔端逼,以及已觸的次數(shù)
# QRTZ_BLOG_TRIGGERS Trigger 作為 Blob 類型存儲(chǔ)(用于 Quartz 用戶用JDBC創(chuàng)建他們自己定制的 Trigger 類型朗兵,JobStore并不知道如何存儲(chǔ)實(shí)例的時(shí)候)
# QRTZ_TRIGGER_LISTENERS 存儲(chǔ)已配置的 TriggerListener的信息
# QRTZ_TRIGGERS 存儲(chǔ)已配置的 Trigger 的信息

重要表說明:

調(diào)度器狀態(tài)表(QRTZ_SCHEDULER_STATE)

說明:集群中節(jié)點(diǎn)實(shí)例信息污淋,Quartz定時(shí)讀取該表的信息判斷集群中每個(gè)實(shí)例的當(dāng)前狀態(tài)。

instance_name:配置文件中org.quartz.scheduler.instanceId配置的名字余掖,如果設(shè)置為AUTO,quartz會(huì)根據(jù)物理機(jī)名和當(dāng)前時(shí)間產(chǎn)生一個(gè)名字寸爆。

last_checkin_time:上次檢入時(shí)間

checkin_interval:檢入間隔時(shí)間

觸發(fā)器與任務(wù)關(guān)聯(lián)表(qrtz_fired_triggers)

存儲(chǔ)與已觸發(fā)的Trigger相關(guān)的狀態(tài)信息礁鲁,以及相聯(lián)Job的執(zhí)行信息。

觸發(fā)器信息表(qrtz_triggers)

trigger_name:trigger的名字,該名字用戶自己可以隨意定制,無強(qiáng)行要求

trigger_group:trigger所屬組的名字,該名字用戶自己隨意定制,無強(qiáng)行要求

job_name:qrtz_job_details表job_name的外鍵

job_group:qrtz_job_details表job_group的外鍵

trigger_state:當(dāng)前trigger狀態(tài)設(shè)置為ACQUIRED,如果設(shè)為WAITING,則job不會(huì)觸發(fā)

trigger_cron:觸發(fā)器類型,使用cron表達(dá)式

任務(wù)詳細(xì)信息表(qrtz_job_details)

說明:保存job詳細(xì)信息,該表需要用戶根據(jù)實(shí)際情況初始化

job_name:集群中job的名字,該名字用戶自己可以隨意定制,無強(qiáng)行要求赁豆。

job_group:集群中job的所屬組的名字,該名字用戶自己隨意定制,無強(qiáng)行要求仅醇。

job_class_name:集群中job實(shí)現(xiàn)類的完全包名,quartz就是根據(jù)這個(gè)路徑到classpath找到該job類的。

is_durable:是否持久化,把該屬性設(shè)置為1魔种,quartz會(huì)把job持久化到數(shù)據(jù)庫中

job_data:一個(gè)blob字段析二,存放持久化job對(duì)象。

權(quán)限信息表(qrtz_locks)

3.2.3 Quartz Scheduler在集群中的啟動(dòng)流程

Quartz Scheduler自身是察覺不到被集群的节预,只有配置給Scheduler的JDBC JobStore才知道叶摄。當(dāng)Quartz Scheduler啟動(dòng)時(shí),它調(diào)用JobStore的schedulerStarted()方法安拟,它告訴JobStore Scheduler已經(jīng)啟動(dòng)了蛤吓。schedulerStarted() 方法是在JobStoreSupport類中實(shí)現(xiàn)的。JobStoreSupport類會(huì)根據(jù)quartz.properties文件中的設(shè)置來確定Scheduler實(shí)例是否參與到集群中糠赦。假如配置了集群会傲,一個(gè)新的ClusterManager類的實(shí)例就被創(chuàng)建、初始化并啟動(dòng)拙泽。ClusterManager是在JobStoreSupport類中的一個(gè)內(nèi)嵌類淌山,繼承了java.lang.Thread,它會(huì)定期運(yùn)行顾瞻,并對(duì)Scheduler實(shí)例執(zhí)行檢入的功能艾岂。Scheduler也要查看是否有任何一個(gè)別的集群節(jié)點(diǎn)失敗了。檢入操作執(zhí)行周期在quartz.properties中配置朋其。

3.2.4 偵測(cè)失敗的Scheduler節(jié)點(diǎn)

當(dāng)一個(gè)Scheduler實(shí)例執(zhí)行檢入時(shí)王浴,它會(huì)查看是否有其他的Scheduler實(shí)例在到達(dá)他們所預(yù)期的時(shí)間還未檢入。這是通過檢查SCHEDULER_STATE表中Scheduler記錄在LAST_CHEDK_TIME列的值是否早于org.quartz.jobStore.clusterCheckinInterval來確定的梅猿。如果一個(gè)或多個(gè)節(jié)點(diǎn)到了預(yù)定時(shí)間還沒有檢入氓辣,那么運(yùn)行中的Scheduler就假定它(們) 失敗了。

3.2.5 從故障實(shí)例中恢復(fù)Job

當(dāng)一個(gè)Sheduler實(shí)例在執(zhí)行某個(gè)Job時(shí)失敗了袱蚓,有可能由另一正常工作的Scheduler實(shí)例接過這個(gè)Job重新運(yùn)行钞啸。要實(shí)現(xiàn)這種行為,配置給JobDetail對(duì)象的Job可恢復(fù)屬性必須設(shè)置為true(job.setRequestsRecovery(true))喇潘。如果可恢復(fù)屬性被設(shè)置為false(默認(rèn)為false)体斩,當(dāng)某個(gè)Scheduler在運(yùn)行該job失敗時(shí),它將不會(huì)重新運(yùn)行颖低;而是由另一個(gè)Scheduler實(shí)例在下一次觸發(fā)時(shí)間觸發(fā)絮吵。Scheduler實(shí)例出現(xiàn)故障后多快能被偵測(cè)到取決于每個(gè)Scheduler的檢入間隔(即2.3中提到的org.quartz.jobStore.clusterCheckinInterval)。

3.3 Quartz集群實(shí)例(Quartz+Spring)

3.3.1 依賴包

Quartz依賴

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>${quartz.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>${spring.version}</version>
</dependency>

3.3.2 配置文件

有三類配置文件:數(shù)據(jù)源配置忱屑、quartz基本配置蹬敲、quartz任務(wù)配置

3.3.2.1 數(shù)據(jù)源配置

1.jdbc配置

jdbc配置

quartz.url=jdbc:mysql://10.111.17.78:3306/kec_scheduler?useUnicode=true&characterEncoding=utf8
quartz.username=root
quartz.password=root
quartz.driverClassName=com.mysql.jdbc.Driver

2.數(shù)據(jù)源定義

數(shù)據(jù)源配置

<?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="propertyConfigurer"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:database.properties</value>
            </list>
        </property>
    </bean>
  
    <!-- 數(shù)據(jù)源定義,使用c3p0 連接池 -->
    <bean id="quartzDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
          destroy-method="close">
        <property name="driverClass" value="${quartz.driverClassName}" />
        <property name="jdbcUrl" value="${quartz.url}" />
        <property name="user" value="${quartz.username}" />
        <property name="password" value="${quartz.password}" />
        <property name="initialPoolSize" value="2" />
        <property name="minPoolSize" value="10" />
        <property name="maxPoolSize" value="20" />
        <property name="acquireIncrement" value="2" />
        <property name="maxIdleTime" value="1800" />
    </bean>
  
    <!-- 使用jdbc訪問數(shù)據(jù)庫 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="quartzDataSource" />
    </bean>
</beans>

3.3.2.2 quartz基本配置

quartz基本配置

#==============================================================   
#Configure Main Scheduler Properties   
#============================================================== 
org.quartz.scheduler.instanceName = DimServerQuartz     # 可為任何值暇昂,用在 JDBC JobStore 中來唯一標(biāo)識(shí)實(shí)例,但是所有集群節(jié)點(diǎn)中必須相同
org.quartz.scheduler.instanceId = AUTO                  # 基于主機(jī)名和時(shí)間戳來產(chǎn)生實(shí)例ID
  
#==============================================================   
#Configure ThreadPool   
#============================================================== 
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
  
  
#==============================================================   
#Configure JobStore   
#============================================================== 
org.quartz.jobStore.misfireThreshold = 60000
# JobStoreTX伴嗡,將任務(wù)持久化到數(shù)據(jù)中急波。因?yàn)榧褐泄?jié)點(diǎn)依賴于數(shù)據(jù)庫來傳播 Scheduler 實(shí)例的狀態(tài),你只能在使用 JDBC JobStore 時(shí)應(yīng)用 Quartz 集群瘪校。
# 這意味著你必須使用 JobStoreTX 或是 JobStoreCMT 作為 Job 存儲(chǔ)澄暮;你不能在集群中使用 RAMJobStore。
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
# 根據(jù)選擇的數(shù)據(jù)庫類型不同而不同阱扬,我這里的是mysql赏寇,所以是org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_               # 表前綴
org.quartz.jobStore.maxMisfiresToHandleAtATime=10
org.quartz.jobStore.isClustered = true                # 屬性為 true,你就告訴了 Scheduler 實(shí)例要它參與到一個(gè)集群當(dāng)中
org.quartz.jobStore.clusterCheckinInterval = 3600000  # 調(diào)度實(shí)例失效的檢查時(shí)間間隔价认,檢查間隔3600s
  
#============================================================== 
#Configure DataSource  我通過配置文件引入數(shù)據(jù)源信息嗅定,如2.1中的配置
#==============================================================
# org.quartz.jobStore.dataSource = myDS     # 別名
# org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
# org.quartz.dataSource.myDS.URL = jdbc:mysql://192.168.31.18:3306/test?useUnicode=true&amp;characterEncoding=UTF-8
# org.quartz.dataSource.myDS.user = root
# org.quartz.dataSource.myDS.password = 123456
# org.quartz.dataSource.myDS.maxConnections = 30
  
org.quartz.scheduler.skipUpdateCheck = true                                                 # 不檢查版本更新
# org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingJobHistoryPlugin # 打印步驟信息,可在調(diào)試時(shí)使用
org.quartz.plugin.shutdownhook.class = org.quartz.plugins.management.ShutdownHookPlugin     # 停止時(shí)的清理插件
org.quartz.plugin.shutdownhook.cleanShutdown = true

3.3.2.3 quartz任務(wù)配置

quartz任務(wù)配置

<?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">
  
    <!-- 數(shù)據(jù)庫配置文件 -->
    <import resource="classpath:datasource.xml"></import>
         
        <!-- 為了支持使用注解所寫的類 -->
    <bean id="jobFactory" lazy-init="false" autowire="no" class="com.test.scheduler.api.AutoWiredJobFactory"/>  
  
    <bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="jobFactory" ref="jobFactory"/>         <!-- 正常情況下QuartzJobBean是無法使用注解的用踩,若想使用需引入配置渠退,該類的實(shí)現(xiàn)下面會(huì)展示 -->
        <property name="overwriteExistingJobs" value="true"/>  <!-- 是否覆蓋已有job,防止啟動(dòng)時(shí)重復(fù)執(zhí)行停止時(shí)的任務(wù) -->
        <property name="dataSource">                           <!-- 數(shù)據(jù)源配置 -->
            <ref bean="quartzDataSource" />
        </property>
        <property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
        <property name="configLocation" value="classpath:quartz/quartz.properties" />   <!-- quartz基本配置 -->
  
        <property name="triggers">                             <!-- 在此添加自定義的trigger -->
            <list>
                <ref bean="SyncTrigger" />
            </list>
        </property>
    </bean>
                
        <!-- 自定義的trigger -->
    <bean id="SyncTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="SyncJob" />
        <property name="cronExpression" value="0/10 * * * * ?" />   <!-- 每隔10s執(zhí)行一次 -->
    </bean>
  
    <!-- 自定義的job -->
    <bean id="SyncJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <property name="durability" value="true"></property>       <!-- 是否持久化脐彩,集群模式時(shí)需為true -->
        <property name="requestsRecovery" value="true"></property> <!-- 失敗或者重啟時(shí)是否在其他節(jié)點(diǎn)恢復(fù) -->
        <property name="jobClass"                                  <!-- 繼承QuartzJobBean的自定義任務(wù)實(shí)現(xiàn)類 -->
                  value="com.scheduler.api.SyncScheduler.SyncJob">
        </property>
    </bean>
 
</beans>

3.3.3 Job類實(shí)現(xiàn)

自定義的任務(wù)(job)碎乃,需要繼承QuartzJobBean并實(shí)現(xiàn)其中的executeInternal函數(shù),另外為了支持使用注解需要一個(gè)額外的AutoWiredJobFactory類惠奸。

3.3.3.1 Job類

自定義Job

package com.scheduler.api.SyncScheduler;
  
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service;
  
@Service
@DisallowConcurrentExecution  // 不允許并發(fā)執(zhí)行
public class SyncJob extends QuartzJobBean {
  
    @Autowired
    private JdbcTemplate jdbcTemplate;
  
    private Logger logger = LoggerFactory.getLogger(SyncJob.class);
  
    @Override
    public void executeInternal(JobExecutionContext jobexecutioncontext) throws JobExecutionException {
  
        //這里執(zhí)行定時(shí)調(diào)度業(yè)務(wù)
        logger.info("testMethod.......1");
        System.out.println("2--testMethod......."+System.currentTimeMillis()/1000);
    }
}

3.3.3.2 AutoWiredJobFactory類

AutoWiredJobFactory類

package com.scheduler.api;
  
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
  
/**
 * 供 quartz scheduler 使用梅誓,使其支持注解
 */
public class AutoWiredJobFactory extends AdaptableJobFactory {
  
    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;
  
    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //調(diào)用父類方法
        Object jobInstance = super.createJobInstance(bundle);
        //進(jìn)行注入
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

3.4 注意事項(xiàng)

3.4.1 時(shí)間同步問題

Quartz實(shí)際并不關(guān)心你是在相同還是不同的機(jī)器上運(yùn)行節(jié)點(diǎn)。當(dāng)集群放置在不同的機(jī)器上時(shí)佛南,稱之為水平集群梗掰。節(jié)點(diǎn)跑在同一臺(tái)機(jī)器上時(shí),稱之為垂直集群嗅回。對(duì)于垂直集群及穗,存在著單點(diǎn)故障的問題。這對(duì)高可用性的應(yīng)用來說是無法接受的绵载,因?yàn)橐坏C(jī)器崩潰了埂陆,所有的節(jié)點(diǎn)也就被終止了。對(duì)于水平集群娃豹,存在著時(shí)間同步問題焚虱。

節(jié)點(diǎn)用時(shí)間戳來通知其他實(shí)例它自己的最后檢入時(shí)間。假如節(jié)點(diǎn)的時(shí)鐘被設(shè)置為將來的時(shí)間懂版,那么運(yùn)行中的Scheduler將再也意識(shí)不到那個(gè)結(jié)點(diǎn)已經(jīng)宕掉了鹃栽。另一方面,如果某個(gè)節(jié)點(diǎn)的時(shí)鐘被設(shè)置為過去的時(shí)間定续,也許另一節(jié)點(diǎn)就會(huì)認(rèn)定那個(gè)節(jié)點(diǎn)已宕掉并試圖接過它的Job重運(yùn)行谍咆。最簡(jiǎn)單的同步計(jì)算機(jī)時(shí)鐘的方式是使用某一個(gè)Internet時(shí)間服務(wù)器(Internet Time Server ITS)。

3.4.2 節(jié)點(diǎn)爭(zhēng)搶Job問題

因?yàn)镼uartz使用了一個(gè)隨機(jī)的負(fù)載均衡算法私股, Job以隨機(jī)的方式由不同的實(shí)例執(zhí)行摹察。Quartz官網(wǎng)上提到當(dāng)前,還不存在一個(gè)方法來指派(釘住) 一個(gè) Job 到集群中特定的節(jié)點(diǎn)倡鲸。

3.4.3 從集群獲取Job列表問題

當(dāng)前供嚎,如果不直接進(jìn)到數(shù)據(jù)庫查詢的話,還沒有一個(gè)簡(jiǎn)單的方式來得到集群中所有正在執(zhí)行的Job列表峭状。請(qǐng)求一個(gè)Scheduler實(shí)例克滴,將只能得到在那個(gè)實(shí)例上正運(yùn)行Job的列表。Quartz官網(wǎng)建議可以通過寫一些訪問數(shù)據(jù)庫JDBC代碼來從相應(yīng)的表中獲取全部的Job信息优床。

3.5 參考文檔

http://www.cnblogs.com/zhenyuyaodidiao/p/4755649.html (Quartz集群原理及配置應(yīng)用)

http://veiking.iteye.com/blog/2372284 (Quartz在集群劝赔、分布式系統(tǒng)中的應(yīng)用)

http://www.reibang.com/p/14f86c6efe22 (分布式定時(shí)任務(wù)(二))

http://www.reibang.com/p/a518dd3229de (分布式定時(shí)任務(wù)(三))

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市胆敞,隨后出現(xiàn)的幾起案子着帽,更是在濱河造成了極大的恐慌,老刑警劉巖移层,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仍翰,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡观话,警方通過查閱死者的電腦和手機(jī)予借,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來频蛔,“玉大人灵迫,你說我怎么就攤上這事』尴” “怎么了龟再?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)尼变。 經(jīng)常有香客問我利凑,道長(zhǎng),這世上最難降的妖魔是什么嫌术? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任哀澈,我火速辦了婚禮,結(jié)果婚禮上度气,老公的妹妹穿的比我還像新娘割按。我一直安慰自己,他們只是感情好磷籍,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布适荣。 她就那樣靜靜地躺著现柠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪弛矛。 梳的紋絲不亂的頭發(fā)上够吩,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音丈氓,去河邊找鬼周循。 笑死,一個(gè)胖子當(dāng)著我的面吹牛万俗,可吹牛的內(nèi)容都是我干的湾笛。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼闰歪,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼嚎研!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起库倘,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤嘉赎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后于樟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體公条,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年迂曲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了靶橱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡路捧,死狀恐怖关霸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情杰扫,我是刑警寧澤队寇,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站章姓,受9級(jí)特大地震影響佳遣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜凡伊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一零渐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧系忙,春花似錦诵盼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽洁墙。三九已至,卻和暖如春戒财,著一層夾襖步出監(jiān)牢的瞬間热监,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工固翰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留狼纬,地道東北人羹呵。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓骂际,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親冈欢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子歉铝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 概述 了解Quartz體系結(jié)構(gòu) Quartz對(duì)任務(wù)調(diào)度的領(lǐng)域問題進(jìn)行了高度的抽象,提出了調(diào)度器凑耻、任務(wù)和觸發(fā)器這3個(gè)...
    張晨輝Allen閱讀 2,220評(píng)論 2 11
  • 本文參考自Spring官方文檔 34. Task Execution and Scheduling太示。 在程序中常常...
    樂百川閱讀 5,556評(píng)論 0 11
  • 美好的一天类缤,早安![太陽] 向日葵說邻吭,只要你朝著陽光努力向上餐弱,生活便會(huì)因此變得單純而美好。美好的一天開始囱晴,愿你能向...
    蜜思小琳閱讀 258評(píng)論 0 0
  • 計(jì)劃365天畸写,其實(shí)已經(jīng)過去16天了驮瞧。 盒子也很精致,過了40年枯芬,從來沒有在吃穿上面動(dòng)過腦子论笔,就是學(xué)習(xí)學(xué)習(xí),工作工作...
    馬上做閱讀 329評(píng)論 0 0
  • 極度的恐懼足以致命 善用恐懼能推動(dòng)改變 這個(gè)世界 正在殘酷懲罰 不改變的人 去做你恐懼的事情 就是改變的開始
    老徐說啦閱讀 159評(píng)論 0 1