第六章. Job 存储和持久化

转自:本文链接 http://unmi.cc/quartz-job-scheduling-framework-6-1/, 来自 隔叶黄莺 Unmi Blog

第六章. Job 存储和持久化

Quartz 用 JobStores 对 Job、Trigger、calendar 和 Schduler 数据提供一种存储机制。Scheduler 应用已配置的 JobStore 来存储和获取到部署信息,并决定正被触发执行的 Job 的职责。所有的关于哪个 Job 要执行和以什么时间表来执行他们的信息都来自于 JobStore。本章就来看 Quartz 中可用的各种类型的 JobStore,和如何使用他们,以及哪一个能适应你的需求。

"罗马非一日建成"

道格拉斯.亚当斯,《宇宙环游指南》

一. Job 存储

在前面章节中,我们未曾花过任何时间来讨论 Scheduler 的 Job 和 Trigger 是保存在哪儿的。我们也许已经实现了,然而,当你停止了 Scheduler 后,那些有关哪些 Job 已经运行和哪些 Job 没有运行的信息就会丢失掉。实际上,所有的关于正在运行中的 Job 的信息也被销毁。

当程序被重启后,Trigger 和 Job 的信息被加回去,且所有的一切又都正常了。我们假定,有一个 Job 是安排为 5 PM 执行,然而 Scheduler 在这个时间之前的五分钟(4:55 PM) 时停掉了。如果你在 5:05 PM 时重新启动了 Scheduler 的话将会发生什么事情呢?Scheduler 还会记得要在 5 PM 触发这个 Job 的吗?答案就是看它是依赖于你使用的哪种类型的 JobStore,以及是如何对它配置的。

二. Quartz 中的 Job 存储

Quartz 支持对 Scheduler 信息的几种不同类型的存储机制。在 Quartz 中两种可用的 Job 存储类型是:

    · 内存(非持久化) 存储

    · 持久化存储

默认时,我们在前面几章的例子中已经使用了内存存储机制。两种类型都是用来服务于相同的目的:存储 Job 信息。然而他们各自是如何运作的,而且他们提供给 Scheduler 的功能是很不一样的。

·JobStore 接口

Quartz 为所有类型的 Job 存储提供了一个接口。这个接口位于 org.quartz.spi 包中,叫做 JobStore。所有的 Job 存储机制,不管是在哪里或是如何存储他们的信息的,都必须实现这个接口。

JobStore 可以列出太多的方法来,但是 JobStore 接口的 API  可归纳为下面几类:

    ·Job 相关的 API

    ·Trigger 相关的 API

    ·Calendar 相关的 API

    ·Scheduler 相关的 API

Quartz 的使用者几乎从不访问或是查看实现了 JobStore 接口的具体类;他们被 Quartz Scheduler 在运行期间内部使用来获取 Job 和 Trigger 信息。不过很值得练习一下,使你自己能熟悉每一种类型,这样你就能更好的理解这些为你所提供的存储机制,并有助于你在 Quartz 应用中选择一个正确的类型。

三. 使用内存来存储 Scheduler 信息



Quartz 直接可用的配置就是把 Job 和 Trigger 信息存储在内存中的。这个解释了为什么,对于前面章节中的例子,每次我们重启了 Quartz 应用程序后,Scheduler 的状态,包括 Job 和 Trigger 信息都丢失了。每回 Java 虚拟机(JVM) 关闭之后,它所占用的内存就释放回给了操作系统,因此任何关于 Job 和 Trigger 的信息都随 JVM 而丢失。

Quartz 的内存 Job 存储的能力是由一个叫做 org.quartz.simple.RAMJobStore 类提供了,当如我们所说,它实现了 JobStore 接口的。RAMJobStore 是 Quartz 的开箱即用的解决方案。对此,我们的意思是说,除非你改变了配置,否则在任何 Quartz 应用中都将使用 RAMJobStore。相比于其他的,使用这种 JobStore 可带来几个好处。

首先,RAMJobStore 是配置最简单的 JobStore:已给你配置好了的。当你下载并安装 Quartz 后,就已为你配置了使用 RAMJobStore 作为存储机制。你能在默认的 quartz.properties 文件中看到这个,如代码 6.1 所示。

代码 6.1. 没有其他配置时默认的 quartz.properties 文件

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
 
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
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.simpl.RAMJobStore

代码 6.1 中显示了包含在 Quartz 二进制包中的默认的 quartz.properties 文件。当你没在你自己的程序中引入一个 quartz.properties 文件,这个属性文件就会得到使用。你可以从默认的 quartz.properties 文件的最后一行看到,RAMJobStore 是名为 org.quartz.jobStore.class 的配置属性的默认值。甚至是未在 quartz.propterties 中设置 org.quartz.jobStore.class 属性时,RAMJobStore 也是默认所用的 JobStore。这是硬编码到 Scheduler 工厂初始化程序中的。

另一使用 RAMJobStore 是优点是它的速度。因为所有的 Scheduler 信息都保存在计算机内存中,访问这些数据随着电脑而变快。这儿不存在进程外的调用,没有数据库连,仅仅是原始而简单的内存访问。再也找不到比这更快的方式了。

·RAMJobStore 的 Job 易失性

你也许还记得在第四章,"部署 Job" 中讲过,Job 可以配置一个易失性属性。当这个易失性属性设置为 false,Job 将会在应用关闭之间持久化下来。这个属性对于用 RAMJobStore 时是不起作用的;那一行为是显式的为持久性的 JobStore 所保留的。

·配置 RAMJobStore

配置你的 Quartz 应用来使用 RAMJobStore 是非常简单的。假如你正用一个定制的 quartz.properties 文件,而不是来自于 Quartz JAR 文件中的,那么加上这行到你的属性文件中即可:

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

这就你要用 RAMJobStore 所要做的事情,正如我们所说的,没有比这更简单的了。

·加载 Job 到 RAMJobStore

既然 RAMJobStore 的目的是存储 Scheduler 信息,那么那些信息一开始是如何被加载到内存中的呢?你可以用两种方式加载 Job 信息。首先,你能硬编码你的 Job、Trigger、calendar 和 listener 到你的代码中。如第三章,"Hello,Quarz",第四章,"部署 Job" 所指出的,然而,这总是一件很危险的事情,因为这对于维护来说将是个梦魇。任何的改变,即使是微不足道的,都要修改代码然后重新编译。甚至是只改变触发的时间,也要改代码然后重编译。这没什么大不了的,你说呢?那也许对于小小的程序是这样的,但对于有大量的 Job 和 Trigger 的程序却成了一个大问题。

第二种途径是使用 JobInitializationPlugin,这会在第八章,"使用 Quartz 插件" 中详细讨论。这个 Quartz 插件使用一个 XML 文件来加载 Job、Trigger、Calendar 和其他你需要加载的东西。这种方式的优点是,当有改变时只需要对这个 XML 文件作改动,不用改代码,不用重编译,仅用一个文本编辑器。阅读第八章可获得关于 Quartz 插件更多的信息。

·RAMJobStore 的缺点

你要问了,"RAMJobStore 不能全是正面的,对吗?"。没错,确实如此。我们前面提到几个使用 RAMJobStore 的优点。现在,让我们来谈谈它的一个负面的地方:因为计算机的内存是易失性的,当你的 Quartz 程序被停止了,它会把内存释放回操作系统,当然了,伴随着存储在所释放内存里的别的内容就是这些部署信息了。

假如你的程序的 Scheduler 信息需要在程序重启之间能保持着,那么你需要看看持久性的 JobStore 了。

四. 使用持久性的 JobStore

在很多方面,JobStore 有用内存来存储的,还有些使用某种能长期持久的方式来共享相拟的特征。这不该有什么惊奇的,因为他们都服务于同一目的。

RAMJobStore 一样,特久性的 JobStore 有优点也有其缺点。在你选择持久性的 JobStore 之前应该认真理解其利与弊。这节就来解释它们的区别,以及在什么情况下你会希望使用持久性的 JobStore

目前,Quartz 提供了两种类型的持久性 JobStore,每一种类型都有其独特的持久化机制。

持久性 JobStore = JDBC + 关系型数据库

尽管有几种不同的持久化机制可被 Quartz 用于持久化 Scheduler 信息,Quartz 依赖于一个关系型数据库管理系统(RDMS) 来持久化存储。假如你想用某种别的而不是数据库来持久化存储,那么你必须通过实现 JobStore 接口自己构建它。假定你想用文件系统来持久化存储。你可以创建一个类,这个类要实现 JobStore 接口,在本章中,当我们说 "持久化",我们隐式的是说用 JDBC 来持久化 Scheduler 状态到数据库中

Quartz 所带的所有的持久化的 JobStore 都扩展自 org.quartz.impl.jdbcjobstore.JobStoreSupport 类。

·JobStoreSupport

JobStoreSupport 是个抽象类,并实现了 JobStore 接口,在此章前面就讨论过的。它为所有基于 JDBC 的 JobStore 提供了基本的功能。图 6.1 显示了 JobStore 类型的层次关系。

图 6.1. JobStore 类型层次如图 6.1 所描绘的, JobStoreSupport 实现了 JobStore 接口,是作为 Quartz 提供的两个具体的持久性 JobStore 类的基类。
JobStoreSupport 本该名之为 JDBCJobStoreSupport作为这个类的一个更好的名字本应该是 JDBCJobStoreSupport,因为这个类专门是为基于 JDBC 存储方案而设置的,然而,这个名并没有减损到它为持久性 JobStore 所提供的功能。

因为 JobStoreSupport 类是抽象的,因此 Quartz 提供了两种不同类型的具体化的 JobStore,每一个设计为针对特定的数据库环境和配置:

·org.quartz.impl.jdbcjobstore.JobStoreTX

·org.quartz.impl.jdbcjobstore.JobStoreCMT

这两个持久性 JobStore 前面简单讨论过。但现在,我们来讨论两个版本所需要的数据库。

五. 为 Job 存储使用数据库

Quartz 中的持久性 JobStore 有时候就是指 JDBC JobStore,因为他们基本是依赖于一个 JDBC  驱动和一个关系型数据库通信。持久性 JobStore 会用到许多的 JDBC 特性,包括支持事特,锁定和隔离级别,只列了这几个特性罢。

要是我的数据库不支持 JDBC 呢?假如你的数据库不支持 JDBC,那你肯定是碰到什么问题了。并不能全归咎于你运气不好,只是在这之前你还有很多工作要做。你最好是切换到某一种支持的数据库平台上来。假如你的数据库不支持 JDBC,你将需要创建一个新的实现(实现了 JobStore 接口)。你也许想检查一下 Quartz 用户论坛里的用户,看谁是否已经做了这样的工作,并是否愿意共享他们的代码或者至少告诉你实现的方法。

·持久性 JobStore 所支持的数据库

Quartz 中的持久性 JobStore 被设计能与如下数据库平台一同使用:

    ·Oracle

    ·MySQL

    ·MS SQL Server 2000

    ·HSQLDB

    ·PostgreSQL

    ·DB2

    ·Cloudscape/Derby

    ·Pointbase

    ·Informix

    ·Firebird

    ·大多数别的有完全 JDBC 兼容性驱动的 RDBMS

·独立环境中的持久性存储

JobStoreTX 类设计为用于独立环境中。这里的 "独立",我们是指这样一个环境,在其中不存在与应用容器的事物集成。这里并不意味着你不能在一个容器中使用 JobStoreTX,只不过,它不是设计来让它的事特受容器管理。区别就在于 Quartz 的事物是否要参与到容器的事物中去。

·程序容器中的持久性存储

JobStoreCMT 类设计为当你想要程序容器来为你的 JobStore 管理事物时,并且那些事物要参与到容器管理的事物边界时使用。它的名字明显是来源于容器管理的事物(Container Managed Transactions (CMT))。

六. 创建 Quartz 数据库结构

JobStore 是基于 JDBC 的,它需要一个数据用于 Scheduler 信息的持久化。Quartz  需要创建 12 张数据库表。表的名字和描述在表 6.1 中列出。

表 6.1. Quartz 需要下列表用于所有的 JDBC 的持久性 JobStore
表名描述
QRTZ_CALENDARS以 Blob 类型存储 Quartz 的 Calendar 信息
QRTZ_CRON_TRIGGERS存储 Cron Trigger,包括 Cron 表达式和时区信息
QRTZ_FIRED_TRIGGERS存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息
QRTZ_PAUSED_TRIGGER_GRPS存储已暂停的 Trigger 组的信息
QRTZ_SCHEDULER_STATE存储少量的有关 Scheduler 的状态信息,和别的 Scheduler 实例(假如是用于一个集群中)
QRTZ_LOCKS存储程序的非观锁的信息(假如使用了悲观锁)
QRTZ_JOB_DETAILS存储每一个已配置的 Job 的详细信息
QRTZ_JOB_LISTENERS存储有关已配置的 JobListener 的信息
QRTZ_SIMPLE_TRIGGERS存储简单的 Trigger,包括重复次数,间隔,以及已触的次数
QRTZ_BLOG_TRIGGERSTrigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候)
QRTZ_TRIGGER_LISTENERS存储已配置的 TriggerListener 的信息
QRTZ_TRIGGERS存储已配置的 Trigger 的信息

中表 6.1 中,所有的表都是以前缀 QRTZ_ 开始。这是默认的,但是你可以通过在 quartz.properties 文件中提供一个替代的前缀来改变它。如果你对不同的 Scheduler 实例使用了多套的表,那么改变这个前缀则是必须的。这在你需要用到多个非集群的 Scheduler,但只想用一个单独的数据库实例时也是要做的。

·安装 Quartz 数据库表

Quartz 包括了所有被支持的数据库平台的 SQL 脚本。你能在 <quartz_home>/docs/dbTables 目录下找到那些 SQL 脚本,这里的 <quartz_home> 是解压 Quartz 分发包后的目录。

大约有 18 种不同的数据库平台的脚本。这差不多能覆盖到你所能想出来的任何数据库。假如你的不在其列,你可以使用其中一个已存在的脚本,略做修改以适应你的数据库平台。

要安装必须的数据库表,先打开那个专为你的数据库平台制作的 .sql 文件并用你喜爱的查询工具执行其中的命令。比如说是 MS SQL Server,你需要用数据库所带的查询分析器(Quary Analyzer) 运行文件 tables_sqlServer.sql 中的命令。SQL 命令不负责创建数据库。你还要特别留意 SQL 文件最前面的注释。通常,在运行命令之前都必须执行几条指令。例如,还是 MS SQL Server 的 SQL 文件,你需要修改文件顶端的这条命令,填入数据库名称,在你刚创建它的时候还是一个空数据库。

USE [enter_db_name_here]

SQL 文件创建了必须的表结构,还给表加上了基本的约束和索引。在本章后面,我们会讨论如何通过对表结构做些额外的变动来改进性能。

QuartzFigure6.1.jpg


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值