我是靠谱客的博主 贪玩白昼,最近开发中收集的这篇文章主要介绍Quartz与spring集成及集群环境配置Quartz与spring集成及集群环境配置一、Quartz简介二、Quartz任务调度的基本实现原理三、quartz集群原理四、Quartz集群实例(与集成spring),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Quartz与spring集成及集群环境配置

一、Quartz简介

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Quartz是一个完全由java编写的开源作业调度框架。简单的使用方法创建一个实现org.quartz.Job接口的java类。实现Job接口包含的唯一方法:

public void execute(JobExecutionContext context)throws JobExecutionException;

在你的Job接口实现类里面,添加一些逻辑到execute()方法。

二、Quartz任务调度的基本实现原理

作为一个优秀的调度框架,Quartz具有以下特点

1)强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求;

2)灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式;

3)分布式和集群能力,Terracotta收购后在原来功能基础上作了进一步提升。本文将对该部分相加阐述。

1. Quart的核心元素及介绍

   Quartz对任务调度的领域问题进行了高度的抽象,提出了调度器(Scheduler)、任务(Job)和触发器(Trigger)这3个核心的概念,并在org.quartz通过接口和类对重要的这些核心概念进行描述:

Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中;

JobDetailQuartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job的实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色。

通过该类的构造函数可以更具体地了解它的功用:JobDetail(java.lang.String name, java.lang.String group, java.lang.Class jobClass),该构造函数要求指定Job的实现类,以及任务在Scheduler中的组名和Job名称;

Trigger:是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等;

Calendarorg.quartz.Calendar和java.util.Calendar不同,它是一些日历特定时间点的集合(可以简单地将org.quartz.Calendar看作java.util.Calendar的集合——java.util.Calendar代表一个日历时间点,无特殊说明后面的Calendar即指org.quartz.Calendar)。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。 

                          

                                                            2.1.1  Scheduler结构图                                                                                                图2.1.2 核心元素之间的关系图

2. Quartz的线程视图

Quartz中,有两类线程,Scheduler调度线程和任务执行线程,其中任务执行线程通常使用一个线程池维护一组线程。

 

2.2.1 Quartz线程视图

Scheduler调度线程主要有两个:执行常规调度的线程,和执行misfiredtrigger的线程。常规调度线程轮询存储的所有trigger,如果有需要触发的trigger,即到达了下一次触发的时间,则从任务执行线程池获取一个空闲线程,执行与该trigger关联的任务。Misfire线程是扫描所有的trigger,查看是否有misfiredtrigger,如果有的话根据misfire的策略分别处理(fire now OR wait for the next fire)。

3. Quartz Job数据存储

Quartz中的trigger和job需要存储下来才能被使用。Quartz中有两种存储方式:RAMJobStore,JobStoreSupport,其中RAMJobStore是将trigger和job存储在内存中,而JobStoreSupport是基于jdbc将trigger和job存储到数据库中。RAMJobStore的存取速度非常快,但是由于其在系统被停止后所有的数据都会丢失,所以在集群应用中,必须使用JobStoreSupport。

三、quartz集群原理

1. Quartz 集群架构

一个Quartz集群中的每个节点是一个独立的Quartz应用,它又管理着其他的节点。这就意味着你必须对每个节点分别启动或停止。Quartz集群中,独立的Quartz节点并不与另一其的节点或是管理节点通信,而是通过相同的数据库表来感知到另一Quartz应用的,如图3.1.1所示。

 

 3.1.1 Quartz集群架构

2. Quartz集群相关数据库表

因为Quartz集群依赖于数据库,所以必须首先创建Quartz数据库表,Quartz发布包中包括了所有被支持的数据库平台的SQL脚本。这些SQL脚本存放于<quartz_home>/docs/dbTables 目录下。这里采用的Quartz 1.8.4版本,总共12张表,不同版本,表个数可能不同。数据库为mysql,用tables_mysql.sql创建数据库表。全部表如图3.2.1所示,对这些表的简要介绍如图3.2.2所示。

 

3.2.1 Quartz 1.8.4在mysql数据库中生成的表

 

3.2.2 Quartz数据表简介

注意:quartz2.X版本与quartz1.X版本建表不同。

①.调度器状态表(QRTZ_SCHEDULER_STATE)

说明:集群中节点实例信息,Quartz定时读取该表的信息判断集群中每个实例的当前
           状态。

instance_name:配置文件中org.quartz.scheduler.instanceId配置的名字,如果设
           置为AUTO,quartz会根据物理机名和当前时间产生一个名字。

  last_checkin_time:上次检入时间

  checkin_interval:检入间隔时间

②.触发器与任务关联表(qrtz_fired_triggers)

  存储与已触发的Trigger相关的状态信息,以及相联Job的执行信息。

③.触发器信息表(qrtz_triggers)

trigger_name:trigger的名字,该名字用户自己可以随意定制,无强行要求

trigger_group:trigger所属组的名字,该名字用户自己随意定制,无强行要求

  job_name:qrtz_job_details表job_name的外键

  job_group:qrtz_job_details表job_group的外键

  trigger_state:当前trigger状态设置为ACQUIRED,如果设为WAITING,则job不会触
        发

  trigger_cron:触发器类型,使用cron表达式

④.任务详细信息表(qrtz_job_details)

说明:保存job详细信息,该表需要用户根据实际情况初始化

  job_name:集群中job的名字,该名字用户自己可以随意定制,无强行要求。

  job_group:集群中job的所属组的名字,该名字用户自己随意定制,无强行要求。

job_class_name:集群中job实现类的完全包名,quartz就是根据这个路径到
         classpath找到该job类的。

  is_durable:是否持久化,把该属性设置为1,quartz会把job持久化到数据库中

  job_data:一个blob字段,存放持久化job对象。

⑤.权限信息表(qrtz_locks)

说明:tables_oracle.sql里有相应的dml初始化,如图2.4所示。

 

图3.2.3 Quartz权限信息表中的初始化信息

四、Quartz集群实例(与集成spring)

1. Spring不兼容Quartz问题

Spring从2.0.2开始便不再支持Quartz。具体表现在Quartz+Spring把Quartz的Task实例化进入数据库时,会产生Serializable的错误:

<bean id="jobtask" class="org.springframework.scheduling.quartz. MethodInvokingJobDetailFactoryBean ">

<property name="targetObject">

<ref bean="quartzJob"/>

</property>

<property name="targetMethod">

<value>execute</value>

</property>

</bean>

这个MethodInvokingJobDetailFactoryBean类中的methodInvoking方法,是不支持序列化的,因此在把QUARTZ的TASK序列化进入数据库时就会抛错。

首先解决MethodInvokingJobDetailFactoryBean的问题,在不修改Spring源码的情况下,可以避免使用这个类,直接调用JobDetail。但是使用JobDetail实现,需要自己实现MothodInvoking的逻辑,可以使用JobDetail的jobClass和JobDataAsMap属性来自定义一个Factory(Manager)来实现同样的目的。例如,本示例中新建了一个MyDetailQuartzJobBean来实现这个功能。

注意:Spring3.1以版本整合必须使用Quartz2.x的版本,否则会整合会一直报错。 

2. 配置文件quartz.properties

默认文件名称quartz.properties,通过设置"org.quartz.jobStore.isClustered"属性为"true"来激活集群特性。在集群中的每一个实例都必须有一个唯一的"instance id" ("org.quartz.scheduler.instanceId" 属性), 但是应该有相同的"scheduler instance name" ("org.quartz.scheduler.instanceName"),也就是说集群中的每一个实例都必须使用相同的quartz.properties 配置文件。除了以下几种例外,配置文件的内容其他都必须相同:

a.线程池大小。

b.不同的"org.quartz.scheduler.instanceId"属性值(通过设定为"AUTO"即可)。

Quartz.properties配置详解:

#===========================================================================  

#Configure Main Scheduler Properties    

#===========================================================================

org.quartz.scheduler.instanceName: MY_JOB_SCHEDULER

org.quartz.scheduler.instanceId: AUTO  

org.quartz.scheduler.rmi.export: false

org.quartz.scheduler.rmi.proxy: false

#org.quartz.scheduler.wrapJobExecutionInUserTransaction: false

 

#============================================================================

#Configure ThreadPool    

#============================================================================

#线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求)

org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool

#指定线程数,至少为1(无默认值)(一般设置为1-100直接的整数合适)  

org.quartz.threadPool.threadCount: 10

#设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5

org.quartz.threadPool.threadPriority: 5

 

#设置SimpleThreadPool的一些属性  

#设置是否为守护线程  

#org.quartz.threadpool.makethreadsdaemons = false  

#org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true  

#org.quartz.threadpool.threadsinheritgroupofinitializingthread=false

 

#============================================================================

# Configure JobStore

#============================================================================

#org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

#org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX

org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate

 

# Open the cluster configuration

org.quartz.jobStore.isClustered:true

# 设置集群检查间隔20s

org.quartz.jobStore.clusterCheckinInterval = 2000  

org.quartz.jobStore.useProperties:false

# 指定前缀

org.quartz.jobStore.tablePrefix:QRTZ_

# 指定数据源名称

org.quartz.jobStore.dataSource:qzDS

# 信息保存时间 默认值60秒  

org.quartz.jobStore.misfireThreshold: 60000  

 

#============================================================================

# Configure Datasources

#============================================================================

org.quartz.dataSource.qzDS.driver:com.mysql.jdbc.Driver

org.quartz.dataSource.qzDS.URL:jdbc:mysql://localhost:3306/quartz

org.quartz.dataSource.qzDS.user:root

org.quartz.dataSource.qzDS.password:

org.quartz.dataSource.qzDS.maxConnection:10

 

# ===========================================================================    

# Configure SchedulerPlugins 插件属性 配置  

# ===========================================================================  

# 自定义插件    

#org.quartz.plugin.NAME.class = com.swh.MyPluginClass  

#org.quartz.plugin.NAME.propName = propValue  

#org.quartz.plugin.NAME.prop2Name = prop2Value  

#配置trigger执行历史日志(可以看到类的文档和参数列表)  

org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingTriggerHistoryPlugin    

org.quartz.plugin.triggHistory.triggerFiredMessage = Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss MM/dd/yyyy}    

org.quartz.plugin.triggHistory.triggerCompleteMessage = Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy} with resulting trigger instruction code: {9}    

#配置job调度插件  quartz_jobs(jobs and triggers内容)XML文档    

#加载 Job Trigger 信息的类   (1.8之前用:org.quartz.plugins.xml.JobInitializationPlugin)  

#org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin  

#指定存放调度器(Job Trigger)信息的xml文件,默认是classpathquartz_jobs.xml  

#org.quartz.plugin.jobInitializer.fileNames =  my_quartz_job2.xml    

#org.quartz.plugin.jobInitializer.overWriteExistingJobs = false    

#org.quartz.plugin.jobInitializer.failOnFileNotFound = true    

#自动扫描任务单并发现改动的时间间隔,单位为秒  

#org.quartz.plugin.jobInitializer.scanInterval = 10  

#覆盖任务调度器中同名的jobDetail,避免只修改了CronExpression所造成的不能重新生效情况  

#org.quartz.plugin.jobInitializer.wrapInUserTransaction = false  

3. 配置spring-scheduler.xml文件

spring-scheduler.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:context="http://www.springframework.org/schema/context"

xmlns:task="http://www.springframework.org/schema/task"

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.0.xsd">

 

<!-- 扫描定时任务目录 -->

<context:component-scan base-package="com.test.quartz" />

 

<!-- 线程执行器配置,用于任务注册 -->

<bean id="executor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">

<property name="corePoolSize" value="10" />

<property name="maxPoolSize" value="100" />

<property name="queueCapacity" value="500" />

</bean>

 

<!-- 任务配置列表 -->

<bean id="testMethod1" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">

<!-- 指定任务执行类 -->

<property name="jobClass" value="com.test.quartz.MyDetailQuartzJobBean" />

<!-- 任务完成之后是否依然保留到数据库,默认false -->

<property name="durability" value="true" />

</bean>

 

<!-- 任务配置列表 -->

<bean id="testMethod2" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">

<!-- 指定任务执行类 -->

<property name="jobClass" value="com.test.quartz.MyDetailQuartzJobBean" />

<!-- jobDataMap来处理对象或者参数的传递

<property name="jobDataMap">

<map>

<entry key="targetObject" value="com.test.service.Test" />

<entry key="targetMethod" value="execute" />

</map>

</property>-->

<!-- 任务完成之后是否依然保留到数据库,默认false -->

<property name="durability" value="true" />

</bean>

 

<!-- 触发器配置时间指定 (自定义类继承CronTriggerFactoryBean-->

<bean id="testMethod1Trigger" class="com.test.quartz.PersistableCronTriggerFactoryBean">

<property name="jobDetail" ref="testMethod1" />

<!-- cron表达式设置定时任务执行时间 -->

<property name="cronExpression">

<!-- 10秒执行一次 -->

<value>0/5 * * * * ?</value>

</property>

</bean>

 

<!-- 触发器配置时间指定 (自定义类继承CronTriggerFactoryBean-->

<bean id="testMethod2Trigger" class="com.test.quartz.PersistableCronTriggerFactoryBean">

<property name="jobDetail" ref="testMethod2" />

<!-- cron表达式设置定时任务执行时间 -->

<property name="cronExpression">

<!-- 10秒执行一次 -->

<value>0/5 * * * * ?</value>

</property>

</bean>

 

<!-- 普通设置定时任务执行时间(延迟10秒启动,然后每隔1分钟执行一次) <bean id="testMethod1Trigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean"> <property name="jobDetail" ref="testMethod1" />

<property name="startDelay" value="10000" /> <property name="repeatInterval" value="60000" /> </bean> -->

 

<!-- 配置调度任务 -->

<bean id="quartzScheduler" parent="startQuartz">

<property name="autoStartup" value="true" />

<property name="schedulerName" value="quartzName" />

<!-- 关联任务触发器列表 -->

<property name="triggers">

<list>

<ref bean="testMethod1Trigger" />

<ref bean="testMethod2Trigger" />

</list>

</property>

<!-- 关联定时任务列表 -->

<property name="jobDetails">

<list>

<ref bean="testMethod1" />

<ref bean="testMethod2" />

</list>

</property>

</bean>

 

<!--配置调度任务1

<bean id="quartzScheduler1" parent="startQuartz">

<property name="autoStartup" value="true" />

<property name="schedulerName" value="quartzName1" />

 关联任务触发器列表

<property name="triggers">

<list>

<ref bean="testMethod2Trigger" />

</list>

</property>

关联定时任务列表

<property name="jobDetails">

<list>

<ref bean="testMethod2" />

</list>

</property>

</bean>-->

<!-- quartz触发器总管理类 如果将lazy-init='false'那么容器启动就会执行调度程序 -->

<bean id="startQuartz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

<!-- 引入quartz.properties属性文件 -->

<property name="configLocation" value="classpath:quartz.properties" />

<!-- 关联dataSource数据源配置 -->

<property name="dataSource" ref="dataSource" />

<!-- 关联transactionManager事务配置 -->

<property name="transactionManager" ref="transactionManager" />

<!-- 指定job覆盖,如果这个覆盖配置为falsequratz启动以后将以数据库的数据为准,配置文件的修改不起作用 -->

<property name="overwriteExistingJobs" value="true" />

<!--applicationContextSchedulerContextKey: 是org.springframework.scheduling.quartz.SchedulerFactoryBean 这个类中把spring上下 文以key/value的方式存放在了quartz的上下文中了,可以用applicationContextSchedulerContextKey所定义的key得到对应的spring上下文 -->

<property name="applicationContextSchedulerContextKey" value="applicationContext" />

<!-- 自定义类,实现自动注入quartz jobspring上下文 -->

<property name="jobFactory">

<bean class="com.test.quartz.AutowiringSpringBeanJobFactory" />

</property>

</bean>

</beans>

4. 将配置文件加入web.xml

web.xml配置增加:

<!-- spring配置文件位置 -->

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>classpath:spring.xml,classpath:spring-hibernate.xml,
classpath:spring-scheduler.xml</param-value>

</context-param>

5. 自定义类

①.AutowiringSpringBeanJobFactory.java

package com.test.quartz;

 

import java.util.Date;

 

import org.quartz.spi.TriggerFiredBundle;

import org.springframework.beans.factory.config.AutowireCapableBeanFactory;

import org.springframework.context.ApplicationContext;

import org.springframework.context.ApplicationContextAware;

import org.springframework.scheduling.quartz.SpringBeanJobFactory;

 

/**

 * 自定义类,自动装配Quartz Jobs,Spring上下文依赖性

 */

public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {

 

private transient AutowireCapableBeanFactory beanFactory;

 

public void setApplicationContext(ApplicationContext context) {

beanFactory = context.getAutowireCapableBeanFactory();

}

 

@Override

protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {

Object job = super.createJobInstance(bundle);

beanFactory.autowireBean(job);

System.out.println("AutowiringSpringBeanJobFactory----------------" + new Date());

return job;

}

}

②.PersistableCronTriggerFactoryBean.java

package com.test.quartz;

 

import org.springframework.scheduling.quartz.CronTriggerFactoryBean;

import org.springframework.scheduling.quartz.JobDetailAwareTrigger;

 

/**

 * Needed to set Quartz useProperties=true when using Spring classes, because

 * Spring sets an object reference on JobDataMap that is not a String

 *

 * @see http://site.trimplement.com/using-spring-and-quartz-with-jobstore-

 *      properties

 * @see http://forum.springsource.org/showthread.php?130984-Quartz-error-

 *      IOException

 */

public class PersistableCronTriggerFactoryBean extends CronTriggerFactoryBean {

@Override

public void afterPropertiesSet() {

super.afterPropertiesSet();

System.out.println("PersistableCronTriggerFactoryBean-------------------");

// Remove the JobDetail element

getJobDataMap().remove(JobDetailAwareTrigger.JOB_DETAIL_KEY);

}

 

}

③.MyDetailQuartzJobBean.java

package com.test.quartz;

 

import java.util.Date;

 

import org.apache.log4j.Logger;

import org.quartz.JobDetail;

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

import org.quartz.impl.JobDetailImpl;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.scheduling.quartz.QuartzJobBean;

import org.springframework.stereotype.Component;

 

import com.test.service.IOrderSevice;

 

@Component

public class MyDetailQuartzJobBean extends QuartzJobBean {

 

private IOrderSevice orderService;

private Logger log = Logger.getLogger(this.getClass());

 

public IOrderSevice getOrderService() {

return orderService;

}

 

@Autowired

public void setOrderService(IOrderSevice orderService) {

this.orderService = orderService;

}

 

     protected void executeInternal(JobExecutionContext context) throws JobExecutionException {

// Every job has its own job detail

JobDetail jobDetail = context.getJobDetail();

// The name is defined in the job definition

String jobName = ((JobDetailImpl) jobDetail).getName(); // 任务名称

log.error(jobName + " start " + new Date());// 记录定时任务进入时间

// 任务执行

log.error(jobName + " end " + new Date());

}

 

}

 

最后

以上就是贪玩白昼为你收集整理的Quartz与spring集成及集群环境配置Quartz与spring集成及集群环境配置一、Quartz简介二、Quartz任务调度的基本实现原理三、quartz集群原理四、Quartz集群实例(与集成spring)的全部内容,希望文章能够帮你解决Quartz与spring集成及集群环境配置Quartz与spring集成及集群环境配置一、Quartz简介二、Quartz任务调度的基本实现原理三、quartz集群原理四、Quartz集群实例(与集成spring)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部