1 基本信息
摘要:Quartz是一个开源的作业调度框架,它完全由java写成,并设计用于J2SE和J2EE应用中。它提供了巨大的灵活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。它有很多特征,如:数据库支持,集群,插件,EJB作业预构建,JavaMail及其它,支持cron-like表达式等等。其中集群配置一般比较复杂,那么在Quartz中如何配置它的集群特性呢?
作者:武玉厚
查看本文第二部分:http://gocom.primeton.com/modules/newbb/forumtopic19180_9963_40.htm
2 Quartz的集群配置
Quartz是一个开源的作业调度框架,它完全由java写成,并设计用于J2SE和J2EE应用中。它提供了巨大的灵活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。它有很多特征,如:数据库支持,集群,插件,EJB作业预构建,JavaMail及其它,支持cron-like表达式等等。其中集群配置一般比较复杂,那么在Quartz中如何配置它的集群特性呢?
2.1 实现集群的基本原理
目前Quartz最新版本是1.6.0。Quartz是通过借助关系数据库和JDBC作业存储来实现集群管理的。
1. 原理
集群通过故障切换和负载平衡的功能,能给调度器带来高可用性和伸缩性。目前集群只能工作在JDBC-Jobstore (JobStoreTX 或者JobStoreCMT)方式下,从本质上来说,是使集群上的每一个节点通过共享同一个数据库来工作的(Quartz通过启动两个维护线程来维护数据库状态实现集群管理,一个是检测节点状态线程,一个是恢复任务线程)。
负载平衡是自动完成的,集群的每个节点会尽快触发任务。当一个触发器的触发时间到达时,第一个节点将会获得任务(通过锁定),成为执行任务的节点。
故障切换的发生是在当一个节点正在执行一个或者多个任务失败的时候。当一个节点失败了,其他的节点会检测到并且标识在失败节点上正在进行的数据库中的任务。任何被标记为可恢复(任务详细信息的"requests recovery"属性)的任务都会被其他的节点重新执行。没有标记可恢复的任务只会被释放出来,将会在下次相关触发器触发时执行。
2. 集群管理用到的表
--任务详细信息表
- CREATE TABLE qrtz_job_details
- (
- JOB_NAME VARCHAR2(80) NOT NULL,
- JOB_GROUP VARCHAR2(80) NOT NULL,
- DESCRIPTION VARCHAR2(120) NULL,
- JOB_CLASS_NAME VARCHAR2(128) NOT NULL,
- IS_DURABLE VARCHAR2(1) NOT NULL,
- IS_VOLATILE VARCHAR2(1) NOT NULL,
- IS_STATEFUL VARCHAR2(1) NOT NULL,
- REQUESTS_RECOVERY VARCHAR2(1) NOT NULL, --可恢复标记
- JOB_DATA BLOB NULL,
- PRIMARY KEY (JOB_NAME,JOB_GROUP)
- );
--触发器与任务关联表
- CREATE TABLE qrtz_fired_triggers
- (
- ENTRY_ID VARCHAR2(95) NOT NULL,
- TRIGGER_NAME VARCHAR2(80) NOT NULL,
- TRIGGER_GROUP VARCHAR2(80) NOT NULL,
- IS_VOLATILE VARCHAR2(1) NOT NULL,
- INSTANCE_NAME VARCHAR2(80) NOT NULL,
- FIRED_TIME NUMBER(13) NOT NULL,
- STATE VARCHAR2(16) NOT NULL,
- JOB_NAME VARCHAR2(80) NULL,
- JOB_GROUP VARCHAR2(80) NULL,
- IS_STATEFUL VARCHAR2(1) NULL,
- REQUESTS_RECOVERY VARCHAR2(1) NULL, --可恢复标记
- PRIMARY KEY (ENTRY_ID)
- );
--调度器状态表
- TABLE qrtz_scheduler_state
- (
- INSTANCE_NAME VARCHAR2(80) NOT NULL, --调度器实例ID
- LAST_CHECKIN_TIME NUMBER(13) NOT NULL, --上次检查时间
- CHECKIN_INTERVAL NUMBER(13) NOT NULL, --检查时间间隔
- RECOVERER VARCHAR2(80) NULL, --恢复调度器
- PRIMARY KEY (INSTANCE_NAME)
- );
2.2 集群配置
通过设置"org.quartz.jobStore.isClustered"属性为"true"来激活集群特性。在集群中的每一个实例都必须有一个唯一的"instance id" ("org.quartz.scheduler.instanceId" 属性), 但是应该有相同的"scheduler instance name" ("org.quartz.scheduler.instanceName"),也就是说集群中的每一个实例都必须使用相同的quartz.properties 配置文件。除了以下几种例外,配置文件的内容其他都必须相同:
? 不同的线程池大小, ? 不同的"org.quartz.scheduler.instanceId"属性值(这个可以很容易做到,设定为"AUTO"即可)。 ? 注意: 永远不要在不同的机器上运行集群,除非他们的时钟是使用某种形式的同步服务(守护)非常有规律的运行(时钟必须在一分一秒内)来达到同步。还有: 永远不要触发一个非集群的实例,如果其他的实例正在同一个数据库表上运行。你将使你的数据严重腐蚀,出现非预期行为。 ? 示例及详细配置说明,请参照附录Quartz配置文件说明。
3 附录
3.1 Quartz配置文件说明
3.1.1 Quartz配置文件基本说明
文件名称:默认文件名称quartz.properties,可以通过更改系统属性“org.quartz.properties”来加载自定义的配置。 格式:属性文件
3.1.2 Quartz配置文件详细说明
3.1.2.1 Scheduler主要属性的配置
- # Scheduler主要属性的一般定义模式如下:
- #
- # org.quartz.scheduler.instanceName = SCHED_NAME
- # org.quartz.scheduler.instanceId = INSTANCE_ID
- # org.quartz.scheduler.threadName = THREAD_NAME
- # org.quartz.scheduler.rmi.export = false
- # org.quartz.scheduler.rmi.proxy = false
- # org.quartz.scheduler.rmi.registryHost = localhost
- # org.quartz.scheduler.rmi.registryPort = 1099
- # org.quartz.scheduler.rmi.createRegistry = never
- # org.quartz.scheduler.userTransactionURL = USER_TX_LOCATION
- # org.quartz.scheduler.wrapJobExecutionInUserTransaction = JOBS_IN_USER_TX
- # org.quartz.scheduler.idleWaitTime = IDLE_WAIT_TIME
- # org.quartz.scheduler.dbFailureRetryInterval = DB_FAILURE_RETRY_INTERVAL
- # org.quartz.scheduler.classLoadHelper.class = CLASS_LOAD_HELPER_CLASS
- # org.quartz.context.key.SOME_KEY = SOME_VALUE
下面是具体说明:
3.1.2.2 线程池(ThreadPool)的配置
下面是具体说明:
- # 定制一个线程池的一般模式如下:
- #
- # org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
- # org.quartz.threadPool.threadCount = THREAD_COUNT
- # org.quartz.threadPool.threadPriority = THREAD_PRIO
- #
- # 简单线程池(SimpleThreadPool)的选项参数:
- #
- # org.quartz.threadPool.makeThreadsDaemons = DAEMON_THREADS
- # org.quartz.threadPool.threadsInheritGroupOfInitializingThread = INHERIT_GRP
- # org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = INHERIT_LDR
- #
- # or
- #
- # org.quartz.threadPool.class = com.mycompany.goo.FooThreadPool
- # org.quartz.threadPool.somePropOfFooThreadPool = someValue
- #
|