第六章. 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 文件
1 2 3 4 5 6 7 8 9 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 了。
本文链接 https://yanbin.blog/quartz-job-scheduling-framework-6-1/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
你好! 又要麻烦你了.我先把我的具体情况给高手描述一下:
# org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
# org.quartz.threadPool.threadCount = 2
# org.quartz.threadPool.threadPriority = 4
org.quartz.jobStore.misfireThreshold = 60000
我一共是9个JOB需要在scheduler中执行,每个job使用的simpletrigger都是间隔固定的时间去执行,job的trigger开始启动时间是tomcat加载的时间,也就是在程序里面直接new Date()了一下,但是启动以后有的JOB能正常启动,有的就不能正常启动了,在启动的结束后查看他们执行的轨迹,发现没有启动的job nextfiretime 是已经过去的时间,所以后来就不执行了! 而且发现一个频率就是一般间隔时间比较短的就不容易启动,间隔时间长的就没有什么问题,这也与我的tomcat启动时间有关吧.我改如何处理能使没有启动的job在tomcat启动的同时就直接运行? 谢谢!急等!
试着把
org.quartz.threadPool.threadCount
设置大一些,比如20,或30试试。
高手 ,我今天反复试过很多次的,我已经把线程个数设置到20和30了,可是还是偶尔出现启动不能加载的状况,好像不是线程个数的问题了。还有个问题就是在quartz启动的时候是不是线程个数越多将会对整个系统性能有很大的影响呢?
我感觉tomcat在启动加载quartz的时候是tomcat才刚刚开始启动,可是整个系统启动需要比较长的时间,所以等tomcat完全启动,quartz起开始时间已经过去了,所以出现了不能加载呢?但是我有9个job一起启动,有的却能启动,有的却不能启动。真是搞不明白了。请高手指点!
单独启动你的 quartz 应用程序是怎么一种情况呢?试着单独启动,若是没什么异常,再移入到用 Tomcat 来启动,还有问题再去确定是出在哪里。
采用 RAMJobStore 存储时,如果关闭相应的某个虚拟目录的服务(不关闭这个WEB SERVER), JOB还是可以触发,如果再重新启动这个虚拟目录的服务, 就会重新建立一个JOB, 加上原来驻留在RAM的JOB的,就有两个JOB, 如何只保留一个JOB呢?
你所指的虚拟目录具体是什么?
如:tomcat 下有多个 <context/>
一个主机上有多个Web应用。
麻烦您,请教一下,我只有用到一个job,是操作数据库的,如果所查到的东西为空,那么就会抛出异常,现在的问题是,异常我不会做处理,定时每分钟执行一次,当weblogic第一次启动如果数据库里面有我要找的记录,那么程序执行正常,然后我会修改数据库里面该记录的某个字段,为了下次查询不会重复,在这种情况下,紧跟着的第二分钟执行job就会有异常,这是肯定的,因为数据库里面没有我要的记录了
前面说的可能比较烦,我想请教您的是:当我数据库里面有了新记录符合我所查询的条件,我想继续把它们取出来,可是就出问题了,取不出来了,当我重新部署weblogic就会正常了,请问这个怎么解决?
你要确定为什么取不出来记录,是那个 Session 没有提交事务吗?
不好意思高手,不明白你的意思啊,我是菜鸟,刚开始接触Quartz,是因为一个项目需要定时,我的web.xml里面是这个配置的(一个例子默认的配置) <servlet> <servlet-name> QuartzInitializer </servlet-name> <display-name> Quartz Initializer Servlet </display-name> <servlet-class> org.quartz.ee.servlet.QuartzInitializerServlet </servlet-class> <init-param> <param-name>config-file</param-name> <param-value>D:/echannel/echannel/etc/common/quartz.properties</param-value> </init-param> <init-param> <param-name>shutdown-on-unload</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>start-scheduler-on-load</param-name> <param-value>true</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> quartz.properties里面的配置: #============================================================================ # Configure Main Scheduler Properties #============================================================================ org.quartz.scheduler.instanceName = MyTest111 org.quartz.scheduler.instanceId = AUTO #============================================================================ # Configure ThreadPool #============================================================================ org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount = 2 org.quartz.threadPool.threadPriority = 5 #============================================================================ # Configure JobStore #============================================================================ #org.quartz.jobStore.misfireThreshold = 60000 #============================================================================ # Configure Plugins #============================================================================ org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingJobHistoryPlugin org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin org.quartz.plugin.jobInitializer.fileNames = /Jobs.xml org.quartz.plugin.jobInitializer.overWriteExistingJobs = true org.quartz.plugin.jobInitializer.failOnFileNotFound = true org.quartz.plugin.jobInitializer.scanInterval = 3600 org.quartz.plugin.jobInitializer.wrapInUserTransaction = false Jobs.xml里面的配置: <?xml version='1.0' encoding='utf-8'?> <quartz xmlns="http://www.opensymphony.com/quartz/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opensymphony.com/quartz/JobSchedulingData http://www.opensymphony.com/quartz/xml/job_scheduling_data_1_5.xsd" version="1.5"> <job>… Read more »
你要思考重新部署weblogic对记录产生的影响。
为什么重新部署前查不到记录(没有符合条件的记录或没有记录)
部署时会做些什么?
第一次启动如果数据库里面有我要找的记录,那么程序执行正常,然后我会修改数据库里面该记录的某个字段,为了下次查询不会重复,意思就是说我查过一遍的东西不会再查
重新部署不会做什么,就是把weblogic redeploy一下,我的意思是现在如果第一次执行job,当数据库里面有数据的时候就正常。当没有数据的话就抛出异常(这是肯定的)然后接着到了下一个执行时间,这个时候如果数据库里面已经有我要的记录了,还会抛出异常,这就是我的问题,当数据库里面有记录但是job不是第一次执行就会有错,如果是第一次执行就会正常
你要贴出你的异常的详细信息。