隔叶黄莺 The Blog of Unmi

〖隔叶黄莺三四声,挂壁飞瀑千万尘。若是人间无净土,此处为何妙语真! 隔叶黄莺四字,本非取自此句,而有寄寓他意,因见妙语,亦与予心合!〗

BlogJava 首页 新随笔 联系 聚合 管理
  287 Posts :: 107 Stories :: 831 Comments :: 0 Trackbacks
第五章. Cron 触发器及相关内容

我们在上章中有承诺过会花更多时间来讲 Quartz 的 CronTrigger,所以不会让你失望的。SimpleTrigger 对于需要在指定的毫秒处及时执行的作业还是不错的,但是假如你的作业需要更复杂的执行计划时,你也就要 CronTrigger 给你提供更强更灵活的功能。

一. Cron 的快速一课

cron 这一观念是来自于 UNIX 世界。在 UNIX 中,cron 是一个运行于后台的守护程序,它负责所有基于时间的事件。尽管 Quartz 除相同的名字和相似的表达式语法外,并未分享到 UNIX cron 别的东西,我们还是值得花几个段落去理解 cron 背后的历史。我们这里的目标不是搞混 UNIX cron 表达式和 Quartz 的 cron 表达式,但是你应该了解 Quartz 表达式的历史,并探索为什么他们运作起来很像。这儿明显有许多有意图的相似性。

有许多不同版本的 UNIX Cron

你会发现不同版本的 cron,每一种都有些微地差异。我们仅着眼于与 Quartz CronTrigger 的比较,因此我们只讨论不同版本 UNIX cron 共性的东西。

UNIX cron 守护进程每隔一分钟被唤醒一次去检查叫做 crontabs 的配置文件。(CrontabCRONTABLE 的连写,其中配置有 cron 守护进程的作业列表和其他的指令。)守护进程检查存储在 crontabs 中的命令并决定是否有要执行的任务。

·UNIX Cron 的格式

你可以认为 UNIX crontab 是 Trigger 和 Job 的组合,因为它们同时列出来执行计划和要执行的命令(job)。

Cron Expression 的格式

crontab 格式包含六段,前五段为执行计划,第六段为要执行的命令。(Quartz cron 表达式有七段。) 下面这些是执行计划的五个字段:

    ·分 (00-59)

    ·时 (00-23)

    ·日 (1-31)

    ·月 (1-12)

    ·周 (0-6 或 sun-sat)

UNIX cron 格式表达式中允许出现一些特殊的字符,例如星号(*),它用来匹配所有值。这作有一个 UNIX crontab 的例子:

0 8 * * * echo "WAKE UP" 2>$1 /dev/console

这一 crontab 条目在每天早上8点打印字符串 "WAKE UP" 到 UNIX 的设置 /dev/console 上。图 5.1 显示了这个动作。

图 5.1. UNIX Cron 执行 0 8 * * * echo "WAKE UP" 2>$1 /dev/console  表达式

[点击看全图]
QuartzFigure5.1.jpg

2. 使用 Quartz CronTrigger

在现实世界里,作业计划通常比 SimpleTrigger 能支持的复杂得多。CronTrigger 就可用于指定非常复杂的计划,这种计划不错,因为也是我们发现需要这么做的。在我们深入到 CronTrigger 之前,让我们先看一个例子。代码 5.1 所示的是一个使用 CronTrigger (连同一个 Quartz cron 表达式) 来部署前面例子中的 PrintInfoJob。代码中的大部分内容与前面章节的例子相同。唯一不同点是我们使用了 CronTrigger 替代了 SimpleTrigger。正因为如此,我们不得不为它提供了一个 cron 表达式。

代码 5.1. 简单使用 CronTrigger 来部署一个 Job


代码 5.1 中的例子使用了如下 cron 表达式:

0 30 7 ? * MON-FRI

当被调度器解释后,它会引起 Trigger 在星期一至星期五的早上 7:30 被激发。让我们来看看 Quartz CronTrigger 的 cron 表达式的格式。


[版权声明]
本站内文章,如未标注 [转载],均系原创或翻译之作,本人 Unmi 保留一切权利。本站原创及译作未经本人许可,不得用于商业用途及传统媒体。网络媒体可随意转载,或以此为基础进行演译,但务必以链接形式注明原始出处和作者信息,否则属于侵权行为。另对本站转载他处文章,俱有说明,如有侵权请联系本人,本人将会在第一时间删除侵权文章。及此说明,重之之重。
posted on 2008-02-21 23:13 隔叶黄莺 阅读(1905) 评论(13)  编辑  收藏 所属分类: Quartz

Feedback

# re: Quartz Job Scheduling Framework[翻译]第五章. Cron 触发器及相关内容 (第一部分) 2008-02-22 10:49 完美世界私服
不错的````````````````````  回复  更多评论
  

# re: Quartz Job Scheduling Framework[翻译]第五章. Cron 触发器及相关内容 (第一部分) 2008-02-22 22:38 FastUnit
标记备用。“每日一句”很不错啊,我也弄一个~  回复  更多评论
  

# re: Quartz Job Scheduling Framework[翻译]第五章. Cron 触发器及相关内容 (第一部分) 2008-02-26 17:33 ShanLei
拜读大作,受益匪浅,请教一个问题:
使用Quartz的Job.xml管理所执行的Job,在Scheduler执行的过程中,我从Job.xml中删除了一个还没执行的Job,Scheduler没有重新启动,但是那个删除了的Job还是执行了。有什么办法能够不重新启动就能从 Job.xml 中删除Job.  回复  更多评论
  

# re: Quartz Job Scheduling Framework[翻译]第五章. Cron 触发器及相关内容 (第一部分) 2008-02-26 19:00 隔叶黄莺
在 quartz.properties 中有两个重要的属性正是满足你的需求的,如下配置用 JobInitializtionPlugin 插件的片断:

..............................

#============================================================================
# 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.fileName = quartz_jobs.xml
org.quartz.plugin.jobInitializer.failOnFileNotFound = true

org.quartz.plugin.jobInitializer.scanInterval = 5
org.quartz.plugin.jobInitializer.overWriteExistingJobs = true


最后面那两个属性,scanInterval 设置重复扫描 quartz_job.xml 文件(默认的job配置文件名)的间隔,单位为秒(默认为0,表示不自动加载),发现配置有新的 jobDetail 则加载到调度器中执行,如 overWriteExistingJobs 配置为 false(默认为 false),则碰到新的 quartz_job.xml 文件中有与现有调度器中同名的 jobDetail 则不覆盖,这就会出现,只改了job 的 cron 表达式而不能重新生效的情况;把 overWriteExistingJobs 设置为 true,就可以避免出现这种情况,可随时修改 job.xml 动态调整执行计划。    回复  更多评论
  

# re: Quartz Job Scheduling Framework[翻译]第五章. Cron 触发器及相关内容 (第一部分) 2008-02-27 13:33 ShanLei
谢谢你的回答,不过我测试了还是不行啊,详细步骤如下,可否帮我看看啊:
1. 我的 quartz.properties 文件中的设置:
org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin
org.quartz.plugin.jobInitializer.fileName = bin\\Jobs.xml
org.quartz.plugin.jobInitializer.failOnFileNotFound = true
org.quartz.plugin.jobInitializer.validating = false
org.quartz.plugin.jobInitializer.validatingSchema = true
org.quartz.plugin.jobInitializer.scanInterval = 60
org.quartz.plugin.jobInitializer.overWriteExistingJobs = true

2. 我的 Jobs.xml 中有4个 Job 如下:
<job>
<job-detail>
<name>POInvoiceJob</name>
<group>POInvoiceJobJobGP</group>
<description>Job for PO Invoice Report</description>
<job-class>com.viasystems.scheduler.apps.POInvoiceReportJob</job-class>
<volatility>false</volatility>
<durability>false</durability>
<recover>false</recover>
</job-detail>
<trigger>
<cron>
<name>POInvoiceJob_trigger</name>
<group>POInvoiceJobJobGP</group>
<description>Job for PO Invoice Report</description>
<job-name>POInvoiceJob</job-name>
<job-group>POInvoiceJobJobGP</job-group>
<cron-expression>0 18 11 ? * *</cron-expression>
</cron>
</trigger>
</job>

<job>
<job-detail>
<name>ScheduledQuery-Chk_Price</name>
<group>ScheduledQuery</group>
<description>Job for Scheduled Query</description>
<job-class>com.viasystems.scheduler.apps.ScheduledQueryJob</job-class>
<volatility>false</volatility>
<durability>false</durability>
<recover>false</recover>
<job-data-map allows-transient-data="true">
<entry>
<key>queryCode</key>
<value>Chk_Price</value>
</entry>
</job-data-map>
</job-detail>
<trigger>
<cron>
<name>ScheduledQueryTrigger-Chk_Price</name>
<group>ScheduledQuery</group>
<description>Trigger for Scheduled Query</description>
<job-name>ScheduledQuery-Chk_Price</job-name>
<job-group>ScheduledQuery</job-group>
<cron-expression>0 20 11 ? * 2,3,4,5,6</cron-expression>
</cron>
</trigger>
</job>

<job>
<job-detail>
<name>ScheduledQuery-Chk_Selling_Price</name>
<group>ScheduledQuery</group>
<description>Job for Scheduled Query</description>
<job-class>com.viasystems.scheduler.apps.ScheduledQueryJob</job-class>
<volatility>false</volatility>
<durability>false</durability>
<recover>false</recover>
<job-data-map allows-transient-data="true">
<entry>
<key>queryCode</key>
<value>Chk_Selling_Price</value>
</entry>
</job-data-map>
</job-detail>
<trigger>
<cron>
<name>ScheduledQueryTrigger-Chk_Selling_Price</name>
<group>ScheduledQuery</group>
<description>Trigger for Scheduled Query</description>
<job-name>ScheduledQuery-Chk_Selling_Price</job-name>
<job-group>ScheduledQuery</job-group>
<cron-expression>0 23 11 ? * *</cron-expression>
</cron>
</trigger>
</job>

<job>
<job-detail>
<name>ScheduledQuery-No_of_Inv</name>
<group>ScheduledQuery</group>
<description>Job for Scheduled Query</description>
<job-class>com.viasystems.scheduler.apps.ScheduledQueryJob</job-class>
<volatility>false</volatility>
<durability>false</durability>
<recover>false</recover>
<job-data-map allows-transient-data="true">
<entry>
<key>queryCode</key>
<value>No_of_Inv</value>
</entry>
</job-data-map>
</job-detail>
<trigger>
<cron>
<name>ScheduledQueryTrigger-No_of_Inv</name>
<group>ScheduledQuery</group>
<description>Trigger for Scheduled Query</description>
<job-name>ScheduledQuery-No_of_Inv</job-name>
<job-group>ScheduledQuery</job-group>
<cron-expression>0 25 11 ? * *</cron-expression>
</cron>
</trigger>
</job>

3. 第一步:在 11:15 分启动 Scheduler, 第二步:在11:19分删除 Jobs.xml 里面的 Job <ScheduledQuery-Chk_Selling_Price>, 第三步:从 Scheduler 的 CMD 信息里面能看到 org.quartz.xml.JobSchedulingDataProcessor 已经重新读了Job列表,第四步:在11:24 分的 log 文件中却仍然最初设定的 Job 依次执行了,如下:
[INFO] 2008-02-27 11:18:00 Job: <POInvoiceJobJobGP.POInvoiceJob> is executing.
[INFO] 2008-02-27 11:20:00 Job: <ScheduledQuery.ScheduledQuery-Chk_Price> is executing.
[INFO] 2008-02-27 11:20:06 Job: <ScheduledQuery.ScheduledQuery-Chk_Price> completed.
[INFO] 2008-02-27 11:23:00 Job: <ScheduledQuery.ScheduledQuery-Chk_Selling_Price> is executing.
[INFO] 2008-02-27 11:23:03 Job: <ScheduledQuery.ScheduledQuery-Chk_Selling_Price> ended with error - <java.sql.SQLException: ORA-02019: connection description for remote database not found
>

帮我看看是什么问题行吗?我研究了好久了哦。  回复  更多评论
  

# re: Quartz Job Scheduling Framework[翻译]第五章. Cron 触发器及相关内容 (第一部分) 2008-02-27 15:09 隔叶黄莺
删除了 job.xml 中的某个 job 配置我还没去细看是否实现了 deleteJob() 该job的功能,不过推荐一下方法,你可以改你的 cron 表达式,让这个 job 不可能安排去执行,比如表达式写为
<!--该 trigger 永不被触发-->
<cron-expression>0/2 * * * * ? 2099</cron-expression>

把年份设置到 2099,相信在你的有生之年,这个 job 都得不到执行。如果设置成前面的年份,会有警告:

obInitializationPlugin.processFile(398) | Error scheduling jobs: Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.
java.lang.UnsupportedOperationException: Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.
at org.quartz.CronExpression.getTimeAfter(CronExpression.java:1345)

但也能起到一样的效果--永不被执行。

这样做,我担心的一点就是,可能这作业仍会占住一个工作者线程,浪费了资源。

注意,你改了 cron-expression 之后要看看后台会不会报出异常,表达式写错的话,将不会改变原来 job 的执行行为。
  回复  更多评论
  

# re: Quartz Job Scheduling Framework[翻译]第五章. Cron 触发器及相关内容 (第一部分) 2008-02-27 16:25 ShanLei
谢谢。呵呵,这种做法可以达到目的,但是不能被领导接受啊。看来我得继续研究。  回复  更多评论
  

# re: Quartz Job Scheduling Framework[翻译]第五章. Cron 触发器及相关内容 (第一部分) 2008-02-27 16:51 隔叶黄莺
刚刚我查过源代码了,JobInitializationPlugin 在扫描文件后重新加载 job 时只有增加和更新的功能,而不能删除已有 job 的操作。

这样如何,扩展 JobInitializationPlugin,JobSchedulingDataProcessor 实现自己的 processFileAndScheduleJobs() 方法或其他的方法。

这样做,不知道你们领导会否同意。
  回复  更多评论
  

# re: Quartz Job Scheduling Framework[翻译]第五章. Cron 触发器及相关内容 (第一部分) 2008-02-28 11:12 ShanLei
那我来试试这个办法吧。谢谢了,有机会请你吃饭啊。  回复  更多评论
  

# re: Quartz Job Scheduling Framework[翻译]第五章. Cron 触发器及相关内容 (第一部分) 2008-03-20 15:31 水中陌
最近也一直在用Quartz。今天发现一个问题:在Linux操作系统下,上面所提到的代码没有执行。即星期一至星期五的早上7:30Job并没有被触发。这是什么原因呢?  回复  更多评论
  

# re: Quartz Job Scheduling Framework[翻译]第五章. Cron 触发器及相关内容 (第一部分) 2008-03-20 23:42 隔叶黄莺
那可要具体分析是什么原因,记录下日志来分析。  回复  更多评论
  

# re: Quartz Job Scheduling Framework[翻译]第五章. Cron 触发器及相关内容 (第一部分) 2008-08-04 14:30 zxming12345
"代码中的大部门内容": 应该是大部分内容  回复  更多评论
  

# re: Quartz Job Scheduling Framework[翻译]第五章. Cron 触发器及相关内容 (第一部分) 2008-08-04 15:15 隔叶黄莺
OK,改了,谢谢你,赏鉴这么细心。  回复  更多评论
  


标题  
姓名  
主页
验证码 *  
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-08-21 12:50 编辑过
 
 
相关链接:
网站导航: