Quartz-cluster最佳实践

1. 概述

虽然单个Quartz实例能给予你很好的Job调度能力,但它不能满足典型的企业需求,如可伸缩性、高可靠性满足。假如你需要故障转移的能力并能运行日益增多的 Job,Quartz集群势必成为你应用的一部分了。使用 Quartz 的集群能力可以更好的支持你的业务需求,并且即使是其中一台机器在最糟的时间崩溃了也能确保所有的 Job 得到执行。

2. 工作原理

一个 Quartz 集群中的每个节点是一个独立的 Quartz 应用,它又管理着其他的节点。意思是你必须对每个节点分别启动或停止。不像许多应用服务器的集群,独立的 Quartz 节点并不与另一其的节点或是管理节点通信。Quartz 应用是通过数据库表来感知到另一应用的。
图:表示了每个节点直接与数据库通信,若离开数据库将对其他节点一无所知
clip_image001

3. 环境搭建

3.1. 创建quartz数据库表

因为Quartz 集群依赖于数据库,所以必须首先创建Quartz数据库表。Quartz 包括了所有被支持的数据库平台的 SQL 脚本。在 <quartz_home>/docs/dbTables 目录下找到那些 SQL 脚本,这里的 <quartz_home> 是解压 Quartz 分发包后的目录。
这里采用的Quartz 1.6.0版本,总共12张表,不同版本,表个数可能不同。数据库为mysql,用tables_mysql_innodb.sql创建数据库表。

3.2. 配置数据库连接池

1.配置jdbc.properties文件

clip_image003

2.配置applicationContext.xml文件

clip_image005

3.3. 配置集群节点的quartz.properties文件

clip_image007

说明:

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 秒)。

3.4. 创建Job服务类

clip_image009

说明:

因为Job需要持久化到数据库中,CustService必须实现Serializable接口,在这里只是简单打印一下日志。

但是这里有一个问题,如果我们的这个CustService要继承其他的基类,而且基类并没有实现序列化,那这个时候CustService仍然是无法持久化到数据库中的。所以我们需要在CustService外面再加入一层业务引用,CustService当做一个对象注入到这个引用层里面,详细请看代码:

1 业务引用层MyBusiJob.java

clip_image011

2 实现序列化接口Job2

clip_image013

3 任务引导

clip_image015

3.5. 与Spring(2.0.8)集成

applicationContext-quartz.xml

clip_image017

说明:

1这个 MethodInvokingJobDetailFactoryBean ,本来应该采用org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean指定类和方法,但是直接使用会报java.io.NotSerializableException异常,这是因为这个类中的 methodInvoking 方法,是不支持序列化的,因此在把 QUARTZ 的 TASK 序列化进入数据库时就会抛错。而且目前最新版的spring也不支持这个问题。这里我们使用spring 官方论坛经过修改的类替代,可以参考:

http://jira.springframework.org/browse/SPR-3797

3.6. 测试类

clip_image019

测试结果:

几个节点都带有 quartz 任务,此时只有一台 quartz 在运行,另几个节点上的 quartz 没有运行。

此时手动 shutdown 那台运行 QUARTZ ,过了 7 秒左右,另一个节点的 quartz 自动监测到了集群中运行着的 quartz 的 instance 已经 shutdown ,因此 quartz 集群会自动把任一台可用的APP 上启动起一个 quartz job 的任务。

下面两种图片是quartz-cluster正常运行下的任务分发

环境:

WebLogic 10.3.5 + Redhat 5.6 + Quartz 1.6.0 + Spring 2.0.8

clip_image021

clip_image023

4. 部署

有两点需要注意:

1 集群配置文件quartz.properties部署的时候必须要一致

2 集群建立起来之后,如果运行过程中需要修改quartz调度器的策略,例如:原来每5天执行一次任务,现在要改成每半个月执行一次,这个时候要修改所有的配置文件,并且要重新执行上述的数据库脚本!

posted @ 2015-03-17 13:52  Jager  阅读(5842)  评论(0编辑  收藏  举报