概述
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实例中;
●JobDetail:Quartz在每次执行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执行等;
●Calendar:org.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文件,默认是classpath下quartz_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覆盖,如果这个覆盖配置为false,quratz启动以后将以数据库的数据为准,配置文件的修改不起作用 -->
<property name="overwriteExistingJobs" value="true" />
<!--applicationContextSchedulerContextKey: 是org.springframework.scheduling.quartz.SchedulerFactoryBean 这个类中把spring上下 文以key/value的方式存放在了quartz的上下文中了,可以用applicationContextSchedulerContextKey所定义的key得到对应的spring上下文 -->
<property name="applicationContextSchedulerContextKey" value="applicationContext" />
<!-- 自定义类,实现自动注入quartz job到spring上下文 -->
<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)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复