Quartz
2012-04-23 23:08
183 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/standyxu/article/details/84208425
This changes dramatically when you need clustering, fail-over, load-balancing and few other buzz-words. There are several use-cases for that:
So if you think you need to schedule jobs and have some of the requirements above, keep reading. I will show you how to configure Quartz with Spring and fully integrate them. First of all we need a
?
As you might have guessed, Quartz needs some database tables to work with. It does not create them automatically, but SQL scripts for several databases are provided, including H2 which as you can see I am using. I think Flyway is the easiest way to run database scripts on startup:
?
BTW in case you haven't noticed: no, there is no XML in our sample application and yes, we are using Spring.
Let's move on to Quartz:
?
It is nice to know you can inject instance of
?
First unexpected input is
?
Quartz 2.0 ships with a nice internal DSL for creating jobs and triggers in a readable manner. As you can see I am passing an extra
?
Unfortunately each and every Spring bean you want to inject to job has to be explicitly referenced in
You might ask yourself way can't we simply use MethodInvokingJobDetailFactoryBean? Well, first of all because it does not work with persistent job stores. Secondly - because it is unable to pass
BTW if anyone asks you: How many classes does a Java enterprise developer need to print “Hello world!" you can proudly reply: 4 classes, 30 JARs taking 20 MiB of space and a relational database with 10+ tables. Seriously, this is an output of our article here...
Essentially plugins in Quartz are convenient classes wrapping registration of underlying listeners. You are free to write your own plugins but we will focus on existing ones shipped with Quartz.
First some background. Two main abstractions in Quartz are jobs and triggers. Job is a piece of code that we would like to schedule. Trigger instructs the scheduler when this code should run. CRON (e.g. run every Friday between 9 AM and 5 PM until November) and simple (run 100 times every 2 hours) triggers are most commonly used. You associate any number of triggers to a single job.
Believe it or not, Quartz by default provides no logging or monitoring whatsoever of executed jobs and triggers. There is an API, but no built-in logging is implemented. It won't show you that it now executes this particular job due to this trigger firing. So the first thing you should do is adding the following lines to your
?
The first line (and the only required) loads the plugin class
?
You see now why naming your triggers (
There is another handy plugin related to logging:
?
The rule is the same - plugin + extra configuration. See JavaDoc of
?
I have no idea why these plugins aren't enabled by default. After all, if you don't want such a verbose output, you can turn it off in your logging framework. Never mind, I think it is a good idea to have them in place when troubleshooting Quartz execution.
This is a pretty comprehensive plugin. It reads XML file (by default named
?
In the aforementioned article we have been manually adding job to the scheduler:
?
The same can be achieved with XML configuration, just place the following
?
The file supports both simple and CRON triggers and is well described using XML Schema. It is even possible to point out to an XML files somewhere in the file system and periodically scan them for changes (!) (see:
?
Last but not least,
?
As you can see Qurtz ships with few quite interesting plugins. For some reason they aren't described in detail in the official documentation, but they work pretty well and are a valuable addition to scheduler.
The source code with applied plugins is available on GitHub.
Configuring Quartz with JDBCJobStore in Spring
I am starting a little series about Quartz scheduler internals, tips and tricks, this is a chapter 0 - how to configure persistent job store. In Quartz you essentially have a choice between storing jobs and triggers in memory and in a relation database ( Terracotta is a recent addition to the mix). I would say in 90% of the cases when you useRAMJobStorewith Quartz you don't really need Quartz at all. Obviously this storage backend is transient and all your pending jobs and triggers are lost between restarts. If you are fine with that, much simpler and more lightweight solutions are available, including
ScheduledExecutorServicebuilt into JDK and
@Scheduled(cron="*/5 * * * * MON-FRI")in Spring. Can you justify using extra 0,5MiB JAR in this scenario?
This changes dramatically when you need clustering, fail-over, load-balancing and few other buzz-words. There are several use-cases for that:
- single server cannot handle required number of concurrent, long running jobs and the executions need to be split into several machines - but each task must be executed exactly ones
- we cannot afford to run jobs too late - if one server is down, another should run the job on time
- ...or less strictly - the job needs to run eventually - even if the one and only server was down for maintenance, delayed jobs need to be run as soon as possible after restart
So if you think you need to schedule jobs and have some of the requirements above, keep reading. I will show you how to configure Quartz with Spring and fully integrate them. First of all we need a
DataSource:
?
?
Let's move on to Quartz:
?
@Configurationannotated classes into another such class for convenience. Except that - nothing fancy. Note that we need
@DependsOn(Array("flyway"))on Quartz scheduler factory - otherwise the scheduler might start before Flyway fired the migration script with Quartz database tables causing unpleasant errors on startup. The essential bits are
SpringBeanJobFactoryand
schedulerContextAsMap. The special factory makes Spring responsible for creating
Jobinstances. Unfortunately this factory is quite limited which we will see shortly in the following example. First we need a Spring bean and a Quartz job:
?
@BeanPropertyinstead of
@Autowiredor
@Resource. Turns out that
Jobis not really a Spring bean, even though Spring creates an instance of it. Instead Spring discovers required dependencies using available setters. So where does the
msgstring come from? Keep going:
?
"Hello, world!"parameter to the job. This parameter is stored in so called
JobDatain the database per job or per trigger. It will be provided to the job when it is executed. This way you can parametrize your jobs. However when executed our job throws
NullPointerException... Apparently
printerreference was not set and silently ignored. Turns out Spring won't simply look through all the beans available in the
ApplicationContext. The
SpringBeanJobFactoryonly looks into
Jobs'and
Triggers'
JobDataand into so called scheduler context (already mentioned). If you want to inject any Spring bean into
Jobyou must explicitly place a reference to that bean in
schedulerContext:
?
schedulerContextMap. Even worse, if you forget about it, Quartz will silently log NPE at runtime. In the future we will write more robust job factory. But for starters we have a working Spring + Quartz application ready for further experiments, sources as always available under my GitHub account.
You might ask yourself way can't we simply use MethodInvokingJobDetailFactoryBean? Well, first of all because it does not work with persistent job stores. Secondly - because it is unable to pass
JobDatato
Job- so we can no longer distinguish between different job runs. For instance our job printing message would have to always print the same message hard-coded in the class.
BTW if anyone asks you: How many classes does a Java enterprise developer need to print “Hello world!" you can proudly reply: 4 classes, 30 JARs taking 20 MiB of space and a relational database with 10+ tables. Seriously, this is an output of our article here...
Quartz scheduler plugins - hidden treasure
Although briefly described in the official documentation, I believe Quartz plugins aren't known enough, looking at how useful they are.Essentially plugins in Quartz are convenient classes wrapping registration of underlying listeners. You are free to write your own plugins but we will focus on existing ones shipped with Quartz.
LoggingTriggerHistoryPlugin
First some background. Two main abstractions in Quartz are jobs and triggers. Job is a piece of code that we would like to schedule. Trigger instructs the scheduler when this code should run. CRON (e.g. run every Friday between 9 AM and 5 PM until November) and simple (run 100 times every 2 hours) triggers are most commonly used. You associate any number of triggers to a single job.Believe it or not, Quartz by default provides no logging or monitoring whatsoever of executed jobs and triggers. There is an API, but no built-in logging is implemented. It won't show you that it now executes this particular job due to this trigger firing. So the first thing you should do is adding the following lines to your
quartz.properties:
?
LoggingTriggerHistoryPlugin. The remaining lines are configuring the plugin, customizing the logging messages. I found the built-in defaults not very well thought, e.g. they display current time which is already part of the logging framework message. You are free to construct any logging message, see the API for details. Adding these extra few lines makes debugging and monitoring much easier:
?
Demo.Every-few-seconds) and jobs (
Demo.Print-message) is so important.
LoggingJobHistoryPlugin
There is another handy plugin related to logging: ?
LoggingJobHistoryPluginfor details and possible placeholders. Quick look at logs reveals very descriptive output:
?
XMLSchedulingDataProcessorPlugin
This is a pretty comprehensive plugin. It reads XML file (by default named quartz_data.xml) containing jobs and triggers definitions and adds them to the scheduler. This is especially useful when you have a global job that you need to add once. Plugin can either update the existing jobs/triggers or ignore the XML file if they already exist - very useful when JDBCJobStore is used.
?
?
quartz_data.xmlin your CLASSPATH:
?
XMLSchedulingDataProcessorPlugin.setScanInterval(). Guess what is Quartz using to schedule periodic scanning?
?
ShutdownHookPlugin
Last but not least, ShutdownHookPlugin. Small but probably useful plugin that register shutdown hook in the JVM in order to gently stop the scheduler. However I recommend turning
cleanShutdownoff - if the system already tries to abruptly stop the application (typically scheduler shutdown is called by Spring via
SchedulerFactoryBean) or the user hit Ctrl+C - waiting for currently running jobs seems like a bad idea. After all, maybe we are killing the application because some jobs are running for too long/hunging?
?
The source code with applied plugins is available on GitHub.
相关文章推荐
- Quartz与spring2.0集成备忘录
- 【Quartz】配置最简单的集群
- iOS开发UI篇—Quartz2D复杂图形绘制(三) 图片文字,进度条,饼形图,小黄人
- Spring Quartz任务调度
- iOS:quartz2D绘图(绘制渐变图形)
- springboot动态配置定时任务2种方式,整合Quartz多线程并发执行多个定时任务配置
- 2010-11-04 quartz学习笔记四 -给任务传参数以及维持任务的状态
- Quartz入门例子简介 从入门到菜鸟(三)
- quartz工厂
- Quartz与spring整合含源码
- Quartz2D
- Spring与Quartz的整合实现定时任务调度
- Quartz简明教程
- iOS开发UI篇—Quartz2D使用(绘制基本图形)
- JAVA使用quartz添加定时任务,并依赖注入对象
- birt使用quartz框架实现自动生成报表
- Quartz2D使用之基本图形绘制(一)
- 自动任务(quartz定时器)
- Spring使用之:Quartz定时任务为什么会被阻塞
- 【IOS 开发学习总结-OC-65】Quartz 2D绘图(4-2)——绘制文本+设置阴影+使用路径