分布式定時任務(二)

Quartz應用和集群原理分析:

使用的環境版本:spring4.x+quartz2.2.x

****1.1 如何在spring中集成quartz集群****

1.1.1 基于maven項目,需要在pom.xml引入的j依賴為:

<dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
</dependency>

1.1.2 Quartz集群的基本配置信息:命名為quartz.properties

#調度標識名 集群中每一個實例都必須使用相同的名稱
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
#遠程管理相關的配置,全部關閉
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
ThreadPool 實現的類名
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
#線程數量
org.quartz.threadPool.threadCount: 10
#線程優先級 
org.quartz.threadPool.threadPriority: 5
#自創建父線程
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
#容許的最大作業延
org.quartz.jobStore.misfireThreshold: 60000
#ID設置為自動獲取 每一個必須不同 
org.quartz.scheduler.instanceId: AUTO
#數據保存方式為持久化
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
#加入集群
org.quartz.jobStore.isClustered: true
#調度實例失效的檢查時間間隔
org.quartz.jobStore.clusterCheckinInterval: 10000

1.1.3 在項目中加入Quartz的初始化信息: 命名spring-quartz.xml

<?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:mvc="http://www.springframework.org/schema/mvc"
    xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"    xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd 
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context.xsd">
       <bean id="quartzScheduler"        class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
            <!-- 自定義的bean注入類,解決job里面無法注入spring的service的問題 -->
            <property name="jobFactory">
                <bean class="com.fc.sales.control.statistics.job.SpringBeanJobFactory" />
            </property>
            <!-- quartz的數據源 -->
            <property name="dataSource" ref="quartz" />
            <!-- quartz的基本配置信息引入 -->
            <property name="configLocation" value="classpath:quartz.properties"/>
<!-- 調度標識名 -->
            <property name="schedulerName" value="DefaultQuartzScheduler" />
            <!--必須的,QuartzScheduler 延時啟動,應用啟動完后 QuartzScheduler 再啟動 -->
            <property name="startupDelay" value="30" />
            <!-- 通過applicationContextSchedulerContextKey屬性配置spring上下文 -->
            <property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
            <!--可選,QuartzScheduler 啟動時更新己存在的Job,這樣就不用每次修改targetObject后刪除qrtz_job_details表對應記錄了 -->
            <property name="overwriteExistingJobs" value="true" />
            <!-- 設置自動啟動 -->
            <property name="autoStartup" value="true" />
            <!-- 注冊觸發器 -->
            <property  name="triggers">
                <list>
                    <ref bean="orderSyncScannerTrigger" />
                </list>
            </property>
            <!-- 注冊jobDetail -->
            <property name="jobDetails">
                <list>
                    <ref bean="orderSyncDetail" />
                </list>
            </property>
        </bean>  
        <!--配置調度具體執行的方法-->  
        <bean id="orderSyncDetail"  
            class="org.springframework.scheduling.quartz.JobDetailFactoryBean">  
           <property name="jobClass" value="com.fc.sales.control.statistics.job.OrderSyncJob"/>
            <property name="durability" value="true" />    
            <property name="requestsRecovery" value="true" /> 
        </bean> 
        <!--配置調度執行的觸發的時間-->  
  <bean id="orderSyncScannerTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">  
            <property name="jobDetail" ref="orderSyncDetail" />  
            <property name="cronExpression">  
                <!-- 每天上午00:30點執行任務調度 -->  
                <value>0 30 00 * * ?</value>  
            </property>  
        </bean>
  • 1.1.4 在web.xml啟動項中加入spring-quartz.xml文件*
 <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:/spring/spring-quartz.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
  </servlet>
  • 1.1.5 附上對應的解決無法注入的jobFactory的代碼*:

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;
import org.springframework.stereotype.Component;

/**
 * Created by lyndon on 16/9/13.
 */
@Component
public class jobFactory extends AdaptableJobFactory {

    //這個對象Spring會幫我們自動注入進來,也屬于Spring技術范疇.
    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //調用父類的方法
        Object jobInstance = super.createJobInstance(bundle);
        //進行注入,這屬于Spring的技術,不清楚的可以查看Spring的API.
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

****1.2 quartz框架實現分布式定時任務的原理****;
Quartz集群中每個節點都是一個單獨的Quartz應用,它又管理著其他的節點。這個集群需要每個節點單獨的啟動或停止;和我們的應用服務器集群不同,獨立的Quratz節點之間是不需要 通信的。不同節點之間是通過數據庫表來感知另一個應用。只有使用持久的JobStore才能完成Quartz集群。

untitled21.jpg
  • 1.2.1 既然Quartz分布式集群是利用數據庫鎖機制來實現集群環境下的并發控制,我們就需要了解Quratz的數據庫表:可以去官方現在對于版本的sql文件導入。
untitled22.png
  • 1.2.2 Quartz線程模型:
    Quartz中有兩類線程:Scheduler調度線程和任務執行線程。

  • 任務執行線程: Quartz不會在主線程(QuartzSchedulerThread)中處理用戶job。Quratz是將線程管理的職責委托給ThreadPool,一般的設置使用SimpleThreadPool,SimpleThreadPool創建一定數量的工作線程(WorkerThread),當然這樣就意味所有的線程都是異步操作的,所以我們在工作線程的job里面實現業務的時候是沒必要重新去創建一個新的線程的,在Quartz創建工作線程的時候已經完成了異步任務的創建。

  • Scheduler調度線程:QuartzScheduler被創建的時候會創建一個QuratzSchedulerThread實例。

  • 1.2.3 Quartz源碼分析:

  • QuartzSchedulerThreand包含有決定何時下一個Job將被觸發的處理循環,主要的邏輯在其的run()方法中,如下圖:

untitled23.png

由此可知,QuartzSchedulerThread不斷的在獲取trigger,觸發trigger,釋放trigger。
那么具體又是如何獲取trigger的呢,可以從上面的源碼中可以發現:qsRsrcs.getJobStore()返回對象是JobStore ,具體的集群配置參考1.1.2. org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
JobStoreTx繼承自JobStoreSupport,而JobStoreSupport的acquireNextTrigger,triggerFired,releaseAcquiredTrigger方法負責具體trigger相關操作,都必須獲得TRIGGER-ACCESS鎖。核心邏輯在executeInNonManagedTxLock方法中。

untitled24.png

由上代碼可知Quartz集群基于數據庫鎖的同步操作流程如下圖所示:

untitled25.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,881評論 18 139
  • 《分布式任務調度平臺XXL-JOB》 一、簡介 1.1 概述 XXL-JOB是一個輕量級分布式任務調度框架,其核心...
    許雪里閱讀 16,828評論 3 29
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,941評論 6 342
  • “碎片化學習”時代的學習最容易陷入走馬觀花的泥潭。在指尖滑過手機屏幕的時候,全球資訊盡在掌握,然而,這些有用沒用甚...
    劈柴捌哥閱讀 328評論 0 2
  • 無論你在哪,我都在原地等你。世上最優美的愛戀,就是暗戀。 走過七月,邁向八月。在度過炎熱,吃著綠豆冰沙的同時,你有...
    當美訴諸于筆端閱讀 187評論 0 0