概述
虽然单个Quartz实例能给予你很好的Job调度能力,但它不能满足典型的企业需求,如可伸缩性、高可靠性满足。假如你需要故障转移的能力并能运行日益增多的 Job,Quartz集群势必成为你应用的一部分了。使用 Quartz 的集群能力可以更好的支持你的业务需求,并且即使是其中一台机器在最糟的时间崩溃了也能确保所有的 Job 得到执行。
Quartz 中集群如何工作
一个 Quartz 集群中的每个节点是一个独立的 Quartz 应用,它又管理着其他的节点。意思是你必须对每个节点分别启动或停止。不像许多应用服务器的集群,独立的 Quartz 节点并不与另一其的节点或是管理节点通信。Quartz 应用是通过数据库表来感知到另一应用的。
图:表示了每个节点直接与数据库通信,若离开数据库将对其他节点一无所知
创建Quartz数据库表
因为Quartz 集群依赖于数据库,所以必须首先创建Quartz数据库表。Quartz 包括了所有被支持的数据库平台的 SQL 脚本。在 <quartz_home>/docs/dbTables 目录下找到那些 SQL 脚本,这里的 <quartz_home> 是解压 Quartz 分发包后的目录。
这里采用的Quartz 1.6.5版本,总共12张表,不同版本,表个数可能不同。数据库为mysql,用tables_mysql_innodb.sql创建数据库表。
配置数据库连接池
1.配置jdbc.properties文件
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
jdbc.username=root
jdbc.password=kfs
2.配置applicationContext.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.5.xsd" >
<context:component-scan base-package="com.sundoctor"/>
<!-- 属性文件读入 -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
<!-- 数据源定义,使用c3p0 连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="initialPoolSize" value="5" />
<property name="minPoolSize" value="5" />
<property name="maxPoolSize" value="20" />
<property name="acquireIncrement" value="2" />
<property name="maxIdleTime" value="3600" />
<property name="idleConnectionTestPeriod" value="180"/>
<property name="automaticTestTable" value="C3P0TESTTABLE"/>
</bean>
</beans>
创建Job测试服务类
- package com.sundoctor.quartz.cluster.example;
- import java.io.Serializable;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.stereotype.Service;
- @Service("simpleService")
- public class SimpleService implements Serializable{
- private static final long serialVersionUID = 122323233244334343L;
- private static final Logger logger = LoggerFactory.getLogger(SimpleService.class);
- public void testMethod1(){
- //这里执行定时调度业务
- logger.info("testMethod1.......1");
- }
- public void testMethod2(){
- logger.info("testMethod2.......2");
- }
- }
因为Job需要持久化到数据库中,SimpleService必须实现Serializable接口,在这里只是简单打印一下日志。
配置 Quartz 使用集群
1.配置节点的 quartz.properties 文件
org.quartz.scheduler.instanceName = TestScheduler1
org.quartz.scheduler.instanceId = AUTO
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
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.maxMisfiresToHandleAtATime=10
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000
org.quartz.scheduler.instanceName属性可为任何值,用在 JDBC JobStore 中来唯一标识实例,但是所有集群节点中必须相同。
org.quartz.scheduler.instanceId 属性为 AUTO即可,基于主机名和时间戳来产生实例 ID。
org.quartz.jobStore.class属性为 JobStoreTX,将任务持久化到数据中。因为集群中节点依赖于数据库来传播 Scheduler 实例的状态,你只能在使用 JDBC JobStore 时应用 Quartz 集群。这意味着你必须使用 JobStoreTX 或是 JobStoreCMT 作为 Job 存储;你不能在集群中使用 RAMJobStore。
org.quartz.jobStore.isClustered 属性为 true,你就告诉了 Scheduler 实例要它参与到一个集群当中。这一属性会贯穿于调度框架的始终,用于修改集群环境中操作的默认行为。
org.quartz.jobStore.clusterCheckinInterval 属性定义了Scheduler 实例检入到数据库中的频率(单位:毫秒)。Scheduler 检查是否其他的实例到了它们应当检入的时候未检入;这能指出一个失败的 Scheduler 实例,且当前 Scheduler 会以此来接管任何执行失败并可恢复的 Job。通过检入操作,Scheduler 也会更新自身的状态记录。clusterChedkinInterval 越小,Scheduler 节点检查失败的 Scheduler 实例就越频繁。默认值是 15000 (即15 秒)。
2.配置applicationContext-quartz.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="applicationContextSchedulerContextKey" value="applicationContextKey"/>
<property name="configLocation" value="classpath:quartz.properties"/>
<property name="triggers">
<list>
<ref bean="trigger1"/>
<ref bean="trigger2"/>
</list>
</property>
</bean>
<bean id="jobDetail1" class="frameworkx.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="simpleService"/>
<property name="targetMethod" value="testMethod1"/>
<property name="shouldRecover" value="true"/>
</bean>
<bean id="trigger1" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="jobDetail1"/>
<property name="cronExpression" value="0/5 * * ? * * *"/>
</bean>
<bean id="jobDetail2" class="frameworkx.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="simpleService"/>
<property name="targetMethod" value="testMethod2"/>
<property name="shouldRecover" value="true"/>
</bean>
<bean id="trigger2" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="jobDetail2"/>
<property name="startDelay" value="1"/>
<property name="repeatCount" value="100"/>
<property name="repeatInterval" value="1000"/>
</bean>
</beans>
dataSource:项目中用到的数据源,里面包含了quartz用到的12张数据库表;
applicationContextSchedulerContextKey: 是org.springframework.scheduling.quartz.SchedulerFactoryBean这个类中把spring上下 文以key/value的方式存放在了quartz的上下文中了,可以用applicationContextSchedulerContextKey所 定义的key得到对应的spring上下文;
configLocation:用于指明quartz的配置文件的位置
关于Job配置,这里有两点需要注意
MethodInvokingJobDetailFactoryBean
在这里使用牛人修改后的frameworkx.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean,可以参考:http://jira.springframework.org/browse/SPR-3797。直接使用org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean会报java.io.NotSerializableException异常。
shouldRecover
shouldRecover属性必须设置为 true,当Quartz服务被中止后,再次启动或集群中其他机器接手任务时会尝试恢复执行之前未完成的所有任务。
运行Quartz集群
在相同或不同的机器上运行com.sundoctor.quartz.cluster.example.test.MainTest进行测试,在本例中只是简单打印一下日志。
- package com.sundoctor.quartz.cluster.example.test;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- public class MainTest {
- /**
- * @param args
- */
- public static void main(String[] args) {
- ApplicationContext springContext = new ClassPathXmlApplicationContext(new String[]{"classpath:applicationContext.xml","classpath:applicationContext-quartz.xml"});
- }
- }
Quartz 实际并不关心你是在相同的还是不同的机器上运行节点。当集群是放置在不同的机器上时,通常称之为水平集群。节点是跑在同一台机器是,称之为垂直集群。对于垂直集群,存在着单点故障的问题。这对高可用性的应用来说是个坏消息,因为一旦机器崩溃了,所有的节点也就被有效的终止了。
当你运行水平集群时,时钟应当要同步,以免出现离奇且不可预知的行为。假如时钟没能够同步,Scheduler 实例将对其他节点的状态产生混乱。有几种简单的方法来保证时钟何持同步,而且也没有理由不这么做。最简单的同步计算机时钟的方式是使用某一个 Internet 时间服务器(Internet Time Server ITS)。
没什么会阻止你在相同环境中使用集群的和非集群的 Quartz 应用。唯一要注意的是这两个环境不要混用在相同的数据库表。意思是非集群环境不要使用与集群应用相同的一套数据库表;否则将得到希奇古怪的结果,集群和非集群的 Job 都会遇到问题。
假如你让一个非集群的 Quartz 应用与集群节点并行着运行,设法使用 JobInitializationPlugin和 RAMJobStore。
相关推荐
java + quartz实现定时任务,实现集群配置,在集群环境下多节点运行定时Quartz定任务,就会存在重复处理任务的现象,为解决这一问题,下面我将介绍使用 Quartz 的 TASK ( 12 张表)实例化到数据库,基于数据库自动...
本人花费多日时间,亲测有效。所需文件代码完整,只需导入常用开发IDE和mysql数据库即可正常使用。
NULL 博文链接:https://fanshuyao.iteye.com/blog/2392350
针对Quartz与Spring做集群的Demo实例,主要解决了Quartz的JOB序列化问题。 源代码说明: support pkg:扩展Spring与Quartz集成的不足之处。 core pkg: 是自身调度业务的封装 实例运行依赖Oracle数据库,根据quartz...
NULL 博文链接:https://vista-move.iteye.com/blog/2274246
spring 集成quartz定时任务 用数据库实现quartz的集群
NULL 博文链接:https://daoshud1.iteye.com/blog/1955099
spring-cloud-alibaba+dubbo+nacos+quartz以yml配置方式实现
该项目包含helloworld(快速入门)、web(ssh项目快速搭建)、aop(切面编程)、data-redis(redis缓存)、quartz(集群任务实现)、shiro(权限管理)、oauth2(四种认证模式)、shign(接口参数防篡改重放)、encoder(用户...
主要给大家介绍了关于spring boot + quartz集群搭建的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
spring 集成 quartz ,支持集群,job支持spring@Autowired注入及@Resource注入
spring3+ quartz1.8的使用开发,包括quartz在spring的各种配置,以及集群的多种版本数据库脚本,欢迎大家下载。
需要把app部署在多台服务器上,但只能让其中一台服务器的job执行,一台服务器挂了,另一台还能继续执行job,通过网上查找资料,都是java工程的方式,不好部署并测试,经过二天辛苦整合,终于整理成一个单spring web...
本文章是关于springboot集成quartz集群的步骤,LZ亲测。
spring整合quartz使用jdbc存储任务,并配置为quartz集群应用
springboot 集成 quartz 集群 加 sql 等 文章介绍 单机加集群都有 https://blog.csdn.net/weixin_42749765/article/details/88532413
1、项目整体采用Maven分模块构建、SVN版本控制、PowerDesigner数据建模,基于约定标准的项目工程结构,同时讲解了很多主流Maven插件的使用配置。 2、前端页面采用流行的bootstrap、 angularjs 等流行的前端技术,...
Spring(IOC DI AOP 声明式事务处理) SpringMVC(支持Restful风格) Hibernate Validate(参数校验) Mybatis(最少配置方案) Quartz时间调度 统一的异常处理 Redis缓存(ProtoStuff序列化) Redis Sentinel主从高...