Quartz最佳实践

本文来自对http://www.quartz-scheduler.org/documentation/best-practices.html的翻译。html

表示还没用过Quartz,正准备用的,而后在官网上看到了这个,而后以为还挺短的,就翻译一下。java

 

最佳实践数据库

        对生产环境中使用Tips安全

        任务数据映射Tips异步

        触发Tipsspa

        JDBC的JobStore命令行

        daylight savings time(这个翻译是夏令时,因此我不知道怎么翻译了)线程

        任务翻译

        监听器(触发监听器,任务监听器,调度监听器)日志

        经过应用暴露调度器功能

 

生产环境中的建议

 

跳过更新检查

 

Quartz在链接服务的时候包含了一个“更新检查”的特性,这个特性会检查是否有Quartz的新版本可用。

这个个检查是异步进行的,并不会影响Quartz的启动和初始化时间,若是链接失败的话,它会优雅地结束。

若是运行了检查,并且找到了新的版本,将会在Quartz的日志中进行记录。

 

你能够将Quartz配置属性设置org.quartz.scheduler.skipUpdateCheck: true或者将系统属性org.terracotta.quartz.skipUpdateCheck=true(这个是在系统环境或者在java命令行中以-D加入),这样就能够禁用掉Quartz的更新检查。建议你在生产环境的部署中禁用更新检查。

 

任务数据映射的建议

 

在JobDataMap中仅仅存储原始数据类型(包括Strings)

 

在JobDataMap中仅仅存储原始数据类型(包括Strings)能够避免短时间或长期的序列化的问题。

 

使用合并的JobDataMap

 

在任务执行的时候,JobExecutionContext中的JobDataMap做为一个convenience。它是经过在JobDetail中的JobDataMap和Trigger中的JobDataMap合并而来,后者中的值会覆盖前面一个中同名变量的值。

 

当你有一个任务在scheduler中,并且这个任务又会被多个Triggers重复使用,那么你最好把值存在Trigger的JobDataMap中,这样对于每次独立的任务触发时,你就能够为Job提供不一样的数据输入啦。

 

根据以上所述,咱们提出了以下的最佳实践:在调用Job.execute(...)方法时,通常来讲应该从JobExecutionContext中的JobDataMap中解析变量的值,而不是直接从JobDetail的JobDataMap中解析。

 

触发器建议

 

使用TriggerUtils

TriggerUtils:

        提供了一个简单的方法来建立triggers(schedules)

        有不少不一样的方法经过schedules来建立triggers以知足特定的描述,这个要比直接实例化特定类型的triggers(SimpleTrigger,CronTrigger等)而后调用不一样的setter方法来配置它们方便许多

        提供了一个简单的方法来建立日期(好比start/end日期)

        提供了分析triggers的助手(好比计算剩余的触发时间)

 

JDBC JobStore

 

永远都不要直接往Quartz的表中写数据

 

经过SQL直接往数据库中写入scheduling数据而不是经过API会形成一下问题:

        会形成数据腐化(被删除的数据,混乱的数据)

        会形成任务在到达执行点的时候像没有执行就消失了

        会形成当触发时间到来时,而任务还未执行

        可能会形成死锁

        其余奇怪的问题和数据腐化

 

永远不要在同一个数据库中将一个非集群的调度器指向另外一个相同名字的调度器

 

若是你在同一套数据库表中指定了多于一个的调度器实例,而且这些实例并非配置在集群中,那么下面的状况将有可能会发生:

        会形成数据腐化(被删除的数据,混乱的数据)

        会形成任务在到达执行点的时候像没有执行就消失了

        会形成当触发时间到来时,而任务还未执行

        可能会形成死锁

        其余奇怪的问题和数据腐化

 

确保足够的数据源中的链接数量

 

建议将你的数据源链接数配置为配置为线程池中工做线程数加3。若是你的应用还要常常调用scheduler的API,那么你还须要增长额外的链接数量。若是你正在使用JobStoreCMT,那么“未管理的”数据源的最大链接数至少为4。

 

夏令时

 

避免将任务安排在接近夏令时的转移时间

 

注意:本地的时钟向前或者向后转移时和总的时间的细节能够在以下连接中找到:

https://secure.wikimedia.org/wikipedia/en/wiki/Daylight_saving_time_around_the_world.

 

SimpleTriggers不受夏令时的影响,这是由于它们老是在毫秒时刻被精确地触发,而且在进过了精确的毫秒数以后会再次被触发。

 

因为CronTriggers会在给定的时/分/秒被触发,当夏令时转移时到来的时候,它们会受到这些怪事的影响。

 

举一个可能发生的例子,在夏令时的美国时区/位置进行调度的时候,若是使用CronTrigger而且调度的触发时间是在1:00 AM和2:00 AM之间时会发生下列的问题:

 

        1:05 AM也许会发生两次!可能会重复地触发CronTrigger

        2:05 AM也许永远不会发生!可能会遗漏CronTrigger的触发

 

同时,时间和调整量要根据当地位置来调节。

 

其余的触发器类型是根据日历的移动而不是根据确切的时间量来进行的,例如CalenderIntervalTrigger,将会一样地受影响,但不是错过触发或者触发两次,而是将它的触发时间偏移一个小时。

 

Jobs

 

等待条件来到

 

长时间运行的任务会阻止其余任务的运行(若是在线程池中全部的线程都繁忙)。

 

若是你认为须要调用Thread.sleep()这个方法来中止工做线程执行任务,这是一个典型的信号,任务不会完成其他的任务,因它必须等待某些条件的到来(好比某些数据可读)。

 

一个更好的方法是释放线程(退出任务)而且容许其余任务在这个线程执行。任务能够从新调度本身,或者在它退出以前其余任务。

 

抛出异常

 

一个任务的执行方法应该包含在try-catch块中,以此处理可能发生的异常。

 

若是一个任务抛出一个异常,Quartz通常会立刻再执行它(可能会抛出相同的异常)。最好是任务捕获全部它可能遇到的异常并处理它们,而后从新调度本身或其余的任务。

 

可恢复性和幂等性

 

带有"recoverable"的任务会在调度器失败时从新执行。这意味着某些任务的工做将会被执行两次。

 

这意味着在编写任务的时候它的工做应该是幂等的。

 

监听器(TriggerListener,JobListener,SchedulerListener)

 

保持编写简洁高效的监听器

 

 

不建议在监听器中完成大量的工做,由于将要执行任务的线程(或者完成触发和引起另外一个任务等)将会绑定在监听器上。

 

处理异常

 

每一个监听器的方法都应该在try-catch块中处理全部可能的异常。

 

若是一个监听器抛出了一个异常,可能会形成其余的监听器没法被通知到或阻止其余任务的执行等。

 

经过应用来暴露调度器的功能

 

当心安全问题!

 

有的用户经过应用程序接口来暴露Quartz的调度功能。这会很是有用,虽它可能会形成极度的危险。

 

确保你没有错误地容许用户定义他们想要的任何参数和任何类型的任务。例如,Quartz会带有一个预约的任务org.quartz.jobs.NativeJob,这个任务将会在它们定义的任意的本地系统上执行命令。恶意的用户可能会使用这个来控制或者摧毁你的系统。

 

一样的像SendEmailJob之类的任务,而且事实上任何其余的任务均可以被看成恶意用途。

 

若是容许用户定义任意他们想要的任务将会是你的系统遭受各类可能的危害,等同于OWASP和MITRE定义的命令注入攻击等。

相关文章
相关标签/搜索