分享

Quartz使用入门

 ahthzxs 2015-11-03

Quartz简介     

      Quartz最初是由James House在souceforge上创建的一个开源定时调度框架(注:Quartz是石英的意思,受到微量电流时可产生一个固定的震动频率【石英钟原 理】),目前由OpenSymphony【http://www./quartz】提供主机维护服务,可以在上面下载到 最新的Quartz版本。目前使用Quartz的项目有Jboss、JIRA、Spring、Jarkarta等等。

Quartz的工作原理     

      对于一个调度作业,通常有两个因素构成:调度的内容【What】:即调度具体做什么工作,例如是发邮件、系统备份;调度的时间【when】:什么时候工 作,这个又包含两个内容,开始时间【调度第一次执行的时间】,调度时间【在调度开始之后,在什么时间的时候,执行定义的调度工作】。

      Quartz框架提供了Job接口和Trigger接口来分别对应调度的内容和调度时间,由于Job接口和Trigger接口是完全分离的,彼此互不关 心,他们之间要建立某种关联,就需要通过Scheduler来帮助他们建立联系。
Quartz启动时,会初始化一套work线程池,这套线程被用于执行预定义的调度作业。Job和Trigger的关联根据配置的不同【基于内存的调度、 基于数据库的调度】而不同:如果是基于内存的调度,那么Job信息和Trigger信息分别放到两个HashMap中,通过RAMJobSupport来 建立关联;如果是基于数据库的调度,Job信息、Tirgger信息以及它们的关联信息都被存到数据库表中。

和JDK Timer API的比较

       目前已有的调度框架除了Flux Scheduler, Enterprise Batching Queuing等一些商业版本之外,还有JDK自带的Timer和TimerTask,对于商业版本不作任何评估,目前只是对能免费使用的两个常用的调度 框架做一些比较:

JDK Timer/TimerTask和Quartz的比较

JDK Timer/TimerTask Quartz
每个任务一个线程 线程池
只是基于内存的调度 内存&数据库
不能支持很复杂的调度 CronTrigger可以定义复杂的调

Quartz简单调度示例

例子:使用Quartz写一个调度,在控制台上每隔1秒输出Hello

  1. 定义Job【调度作业】,定义的Job类要实现Job接口,在接口里定义了调度作业的具体工作内容,在本例中就是在控制台输出“Hello”,请 注意的是在这个Job里面,并没有定义这个Job在什么时候开始执行,执行的时间间隔,这就将调度和调度作业分离开。
    Java代码  收藏代码
    1. public class SimpleDemoJob implements Job{  
    2. public void execute(JobExecutionContext arg0) throws JobExecutionException {  
    3.          System.out.println("Hello");  
    4.     }  
    5. }   
  2. 实现调度【调度的开始时间、调度的间隔】
    Java代码  收藏代码
    1. public class SimpleScheduler {  
    2.         public static void main(String[] args) throws Exception{  
    3.         new SimpleScheduler().startScheduler();  
    4.     }  
    5.     public void startScheduler()throws Exception{  
    6.         Scheduler scheduler= StdSchedulerFactory.getDefaultScheduler() ;  
    7. JobDetail jobDetail = new JobDetail("anotherJob","SimpleGroup",SimpleDemoJob.class);  
    8.         Trigger trigger = TriggerUtils.makeSecondlyTrigger();  
    9.         trigger.setName("simpleTrgger");  
    10.         trigger.setStartTime(new Date());             
    11.         scheduler.scheduleJob(jobDetail, trigger);  
    12.         scheduler.start();        
    13.       }   
    14. }  
    上面的两段代码完成了一个简单的调度:在控制台输出Hello, 从代码中可以看出Quartz实现了调度时间和调度作业的完全分离,它们之间的关联是通过调用Scheduler.scheduleJob方法来完成的。 同时也可以看出建立一个简单的调度需要有如下几个步骤:
    1. 定义Job;
    2. 定义Trigger;
    3. 获得Scheduler;
    4. 建立JobDetail对象,通过JodDetail对象实例化Job;
    5. Job和Trigger建立关联;
    6. 开始调度 scheduler.start方法。
    需要注意的是第四步,在前面很大的篇幅都是在说Job,第四步多了一个JobDetail,虽然Job是Quartz的核心组件,但是Scheduler 并不直接和Job打交道,而是通过JobDetail来建立关联,这样的好处是:
    1. 可以将Job分组;
    2. 可以通过访问JobDetail的JobDataMap向Job传递一些运行时参数和属性;

示例分析

通过上面的例子,我们了解了完成一个简单调度的流程,这个例子有如下几个特点:

  1. 调度非常简单,通过TriggerUtils.makeSecondlyTrigger来创建一个SimpleTrigger,这个 Trigger每秒会被触发一次。TriggerUtils是一个工具类提供了很多方便的方法用于创建每秒、每分、每小时、每天触发一次的调度,这些 Trigger实际上都是SimpleTrigger,而Quartz本身提供了四种形式的Trigger实现,最常用的是SimpleTrigger和 CronTrigger,SimpleTrigger不能支持很复杂的调度,如果要实现在工作日做什么工作,休息日做什么工作等这样的调度,需要 CronTrigger来完成;
  2. 如果要向Job传递一些运行时参数,可以向JobDataMap中放入参数,在Job的实现类中读取这些参数,JobDataMap是一个 HashMap的实现类。

CronTrigger

      在通常的业务场景中中,调度周期的定义是比较复杂的,有些调度只是在工作日完成,还需要考虑节假日的问题,通过SimpleTrigger很能完成,需要 使用功能更为强大的CronTrigger。

     Cron介绍

      Cron是Unix系统的用于调度的后台守护进程,负责所有基于时间的事件,每隔一分钟去看一次crontab文件,看是否有需要被执行的调度任务。 Cron的调度时间定义是通过Cron表达式的。
       Crontab包含六个字段:分【00-59】、时【00-23】、日【1-31】、月【1-12】、周【0-6】或者【sun-sat】,Cron表达 式允许出现特殊字符:*、/等,第六个字段为要执行的命令。
下面一个Cron命令表示每天早上8点在控制台输出 WAKE UP:
0 8 * * * echo "WAKE UP" 2>$1 /dev/console

      Quartz的Cron表达式

       Quartz的Cron表达式字段被扩展到了七个字段,而且最后一个字段并不是要执行的命令:

Quartz的Cron表达式字段
名称 是否必须 允许值 特殊字符
0-59 , - * /
0-59 , - * /
0-23 , - * /
1-31 , - * / L W C
1-12或JAN-DEC , - * /
1-7或sun-sat , - * / L C #
空 或 1970-2099 , - * /

      关于Cron表达式的字段说明:

  1. 月和周的名称不分大小写;FRI、Fri、fri是一样的;
  2. 关于特殊字符的使用参考Quartz的相关文档,在此不做详细说明。

    例如:有一个调度任务,需要在周一到周五下午16:41执行,则这个Trigger可以按照如下方式建立:

Java代码  收藏代码
  1. Trigger trigger = new CronTrigger("AnotherCronTrigger",null,"0 41 16 ? * MON-FRI");  

   建立这个Trigger之后,和要调度的任务通过Scheduler进行关联就可以完成调度了。

基于数据库的调度

      到目前为止,我们可以认为前面所谈到的所有调度信息都是基于内存的【实际上修改配置项可以将它们修改为基于数据库的】,前面已经谈到过Quartz的一个很重要的优点就是支持基于数据库的调度,假定调度都是基于内存的,那么在系统出现异常或者当机的时候,这些调度信息很有可能就丢失了,使用基于数据库的调度则不会出现这样的情况了,调度信息都被存储在数据库中,系统出现异常,重新启动之后,调度信息就可以重新回复到异常前的状态。

      通过配置quartz.properties可以将调度配置成基于内存的或者是基于数据库的;

Quartz.properties

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.jobStore.misfireThreshold = 60000  

#基于内存的调度
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

数据库的配置

      Quartz支持N种数据库,基本上现在主流的都能很好的支持(Oracle, DB2, SQL Server, MySQL,Derby,FireBird, Infomix, PostgreSQL, HSQLDB)。

  1. 建立数据库表 ,从网上下载Quartz之后,在<quartz_home>/docs/dbTables里面可以找到建立数据库表的脚本,执行即可。
  2. 配置JobStore接口
    前面说过,在配置项中定义JobStore接口是RAMJobStore,就可以将调度配置为基于内存的,要将调度配置成基于数据库的则需要将RAMJobStore修改成org.quartz.impl.jdbcjobstore.JobStoreTX 或者org.quartz.impl.jdbcjobstore.JobStoreCMT,二者的区别在于:第一个是运行在独立的环境下的,第二个需要容器来管理JobStore事务。
  3. 配置数据源信息
    org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
    org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.MSSQLDelegate
    org.quartz.dataSource.myDS .driver = net.sourceforge.jtds.jdbc.Driver  
    org.quartz.dataSource.myDS .URL = jdbc:jtds:sqlserver://localhost:1433/gjl  
    org.quartz.dataSource.myDS .user = sa  
    org.quartz.dataSource.myDS .password = passwd  
    org.quartz.dataSource.myDS .maxConnections = 10
    org.quartz.jobStore.DataSource = myDS
    以上就是数据源的配置信息,实际上也就是数据库的连接信息,和其他的配置没有什么很大的区别,需要注意的是最后的一个配置项:org.quartz.jobStore.DataSource = myDS ,用红色标出了,这些都应该保证一致。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多