http://www.quartz-scheduler.o...

1.应用Quartz

  • Quartz Scheduler一旦敞开,无奈重启

    • 须要从新实例化
  • 提供了暂停状态
SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();Scheduler sched = schedFact.getScheduler();sched.start();// define the job and tie it to our HelloJob classJobDetail job = newJob(HelloJob.class)  .withIdentity("myJob", "group1")  .build();// Trigger the job to run now, and then every 40 secondsTrigger trigger = newTrigger()  .withIdentity("myTrigger", "group1")  .startNow()  .withSchedule(simpleSchedule()                .withIntervalInSeconds(40)                .repeatForever())  .build();// Tell quartz to schedule the job using our triggersched.scheduleJob(job, trigger);

2.Quartz API,JOB和Trigger

http://www.quartz-scheduler.o...

  • JobBuilder-用于定义/构建JobDetail实例,该实例定义Jobs实例。
  • JobDetail-用于定义Job的实例。
  • Job-由您心愿由调度程序执行的组件实现的接口。
  • TriggerBuilder-用于定义/构建触发器实例。
  • Trigger-定义将在其上执行给定作业的时间表的组件。

Scheduler

一个Scheduler的生命周期是由它的创作范畴,通过SchedulerFactory来调用他的创立和敞开。

创立Scheduler后,就能够应用它

  • 增加
  • 删除
  • 列出Job和Trigger

以及执行其余与打算相干的操作(例如暂停触发器)。然而,调度程序在应用starter之前实际上不会对任何trigger执行工作起作用

Builder的演示

 // define the job and tie it to our HelloJob class  JobDetail job = newJob(HelloJob.class)      .withIdentity("myJob", "group1") // name "myJob", group "group1"      .build();  // Trigger the job to run now, and then every 40 seconds  Trigger trigger = newTrigger()      .withIdentity("myTrigger", "group1")      .startNow()      .withSchedule(simpleSchedule()          .withIntervalInSeconds(40)          .repeatForever())                  .build();  // Tell quartz to schedule the job using our trigger  sched.scheduleJob(job, trigger);

Jobs and Triggers

Job是一个接口

public interface Job {  public void execute(JobExecutionContext context)    throws JobExecutionException;}

当一个Jobs被触发执行的时候,会调用execute,在一个Scheduler的worker的线程外面。

JobExecutionContext提供了运行的时候须要的一些变量

JobDetailJob加到Scheduler的时候,他能蕴含一些属性用来设置给job的,比如说JobDataMap,他能让咱们存储一些状态信息给咱们Job

Trigger用来触发执行工作,也蕴含了JobDataMap,他能通知咱们什么时候执行工作。默认有SimpleTriggerCronTrigger

SimpleTriggere

如果您须要一次性执行(在给定时刻只执行一个作业),或者如果您须要在给定工夫触发一个作业,并让它反复N次,两次执行之间的提早为T,那么SimpleTrigger十分不便。

如果您心愿基于相似日历的日程安排(如每个周五中午或每个月的第10天的10:15)进行触发,那么CronTrigger十分有用。

Identifies

JobKey and TriggerKey

groups

3.Job和JobDetail的详情

  // define the job and tie it to our HelloJob class  JobDetail job = newJob(HelloJob.class)      .withIdentity("myJob", "group1") // name "myJob", group "group1"      .build();  // Trigger the job to run now, and then every 40 seconds  Trigger trigger = newTrigger()      .withIdentity("myTrigger", "group1")      .startNow()      .withSchedule(simpleSchedule()          .withIntervalInSeconds(40)          .repeatForever())                  .build();  // Tell quartz to schedule the job using our trigger  sched.scheduleJob(job, trigger);
public class HelloJob implements Job {  public HelloJob() {  }  public void execute(JobExecutionContext context)    throws JobExecutionException  {    System.err.println("Hello!  HelloJob is executing.");  }}

每次执行的时候,都会在调用execute前创立其实例,执行实现后会删除和垃圾回收。

  • 要求Job实现类必须有无参结构
  • Job下面定义的状态数据字段没有什么用,因为每次都是新的,而且会被革除

    • 这种存储信息最好应用JobDataMap

JobDataMap

// define the job and tie it to our DumbJob classJobDetail job = newJob(DumbJob.class)  .withIdentity("myJob", "group1") // name "myJob", group "group1"  .usingJobData("jobSays", "Hello World!")  .usingJobData("myFloatValue", 3.141f)  .build();
public class DumbJob implements Job {    public DumbJob() {    }    public void execute(JobExecutionContext context)      throws JobExecutionException    {      JobKey key = context.getJobDetail().getKey();      JobDataMap dataMap = context.getJobDetail().getJobDataMap();      String jobSays = dataMap.getString("jobSays");      float myFloatValue = dataMap.getFloat("myFloatValue");      System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);    }  }
  • 存储的对象会被序列化,须要留神版本问题
  • 如果keyName对应对应的set+Keyname那么会调用对应的Job的setter办法

JobExecutionContext可能帮咱们合并TriggerJobDetail上的JobDataMap外面的值,Trigger上的key会笼罩JobDetail上的

public class DumbJob implements Job {  public DumbJob() {  }  public void execute(JobExecutionContext context)    throws JobExecutionException  {    JobKey key = context.getJobDetail().getKey();    JobDataMap dataMap = context.getMergedJobDataMap();  // Note the difference from the previous example    String jobSays = dataMap.getString("jobSays");    float myFloatValue = dataMap.getFloat("myFloatValue");    ArrayList state = (ArrayList)dataMap.get("myStateData");    state.add(new Date());    System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);  }}

Job 实例

触发器触发,会加载对应的JobDetail,并且通过配置的Jobfactory来实例化对应的Job

并且尝试在对应的JobFactory上调用与JobDataMap中的键名匹配的setter办法。

工作状态和并发执行

@DisallowConcurrentExecution

这个用来加到Job类下面,用来通知Quartz不要同时执行同一个Job

@PersistJobDataAfterExecution

也是加到Job类下面,通知Quartz执行实现后(没有出现异常),更新JobDataMap的存储,用来通知下次执行的时候可能获取以后设置的值。

其余

  • 持久性

    • 如果Job不会再应用了,会主动从Scheduler删除,这个和Trigger相干
RequestsRecovery - if a job “requests recovery”, and it is executing during the time of a ‘hard shutdown’ of the scheduler (i.e. the process it is running within crashes, or the machine is shut off), then it is re-executed when the scheduler is started again. In this case, the JobExecutionContext.isRecovering() method will return true.

4.对于Trigger

像Job一样,Trigger也很容易应用,然而的确蕴含各种自定义选项,在充分利用Quartz之前,您须要理解它们并理解它们。同样,如前所述,您能够抉择不同类型的Trigger来满足不同的调度需要。

通用的Trigger属性

  • JobKey:

    • 触发器执行的时候执行的job标识
  • startTime:标识触发器失效工夫
  • endTime:触发器完结工夫

优先级

当咱们触发器很多,Quartz线程池的工作线程很少的时候,可能没有足够的资源共事启动所有要触发的触发器,如果没有指定优先级,会应用默认优先级5

优先级配置反对任何整数值负数或者正数

留神:只有触发工夫雷同的时候才会比拟优先级,打算在10:59触发的触发器将始终在打算在11:00触发的触发器之前触发。

留神:当检测到触发器的作业须要复原时,其复原的排定的优先级与原始触发器雷同。

CronTrigger trigger = TriggerBuilder.newTrigger().  withIdentity(getTriggerKey(scheduleJobEntity.getId())).  withSchedule(cronScheduleBuilder).  startAt(scheduleJobEntity.getStartTime()).  endAt(scheduleJobEntity.getEndTime()).  withPriority(10).  build();

日历

日历对于从触发器的触发打算中排除时间段很有用。例如,您能够创立一个触发器,该触发器在每个工作日的上午9:30触发一个工作,而后增加一个排除所有企业假期的日历。

public interface Calendar {  public boolean isTimeIncluded(long timeStamp);  public long getNextIncludedTime(long timeStamp);}

上述接口都是毫秒,为了不便Quartz还提供了HolidayCalendar来提供终日的排除

HolidayCalendar cal = new HolidayCalendar();cal.addExcludedDate( someDate );cal.addExcludedDate( someOtherDate );sched.addCalendar("myHolidays", cal, false);Trigger t = newTrigger()  .withIdentity("myTrigger")  .forJob("myJob")  .withSchedule(dailyAtHourAndMinute(9, 30)) // execute job daily at 9:30  .modifiedByCalendar("myHolidays") // but not on holidays  .build();// .. schedule job with triggerTrigger t2 = newTrigger()  .withIdentity("myTrigger2")  .forJob("myJob2")  .withSchedule(dailyAtHourAndMinute(11, 30)) // execute job daily at 11:30  .modifiedByCalendar("myHolidays") // but not on holidays  .build();// .. schedule job with trigger2

5.SimpleTrigger

如果您须要在特定的工夫或特定的工夫精确执行一次作业,而后在特定的工夫距离反复执行一次, SimpleTrigger应该能够满足您的调度需要。例如,如果您想让触发器在2015年1月13日上午11:23:54触发,或者您想在那时触发,而后每十秒钟再触发五次。

  • 属性

    • 开始工夫和完结工夫
    • 反复计数

      • 能够为零,正整数,常量值SimpleTrigger.REPEAT_INDEFINITELY
    • 反复距离

      • 必须为零或者正值,代表为毫秒数
      • 距离为0

DateBuilder类有助于计算触发器的触发工夫,取决于咱们的开始和完结工夫

开始和完结工夫优先级大于反复计数

为特定工夫建设触发器,不要反复

import static org.quartz.TriggerBuilder.*;import static org.quartz.SimpleScheduleBuilder.*;import static org.quartz.DateBuilder.*:
SimpleTrigger trigger = (SimpleTrigger) newTrigger()  .withIdentity("trigger1", "group1")  .startAt(myStartTime) // some Date  .forJob("job1", "group1") // identify job with name, group strings  .build();

为特定工夫建设触发器,每十秒反复十次

trigger = newTrigger()  .withIdentity("trigger3", "group1")  .startAt(myTimeToStartFiring)  // if a start time is not given (if this line were omitted), "now" is implied  .withSchedule(simpleSchedule()                .withIntervalInSeconds(10)                .withRepeatCount(10)) // note that 10 repeats will give a total of 11 firings  .forJob(myJob) // identify job with handle to its JobDetail itself                     .build();

建设一个触发器,该触发器将在将来五分钟触发一次

trigger = (SimpleTrigger) newTrigger()  .withIdentity("trigger5", "group1")  .startAt(futureDate(5, IntervalUnit.MINUTE)) // use DateBuilder to create a date in the future  .forJob(myJobKey) // identify job with its JobKey  .build();

建设一个当初将触发的触发器,而后每五分钟反复一次,直到22:00

trigger = newTrigger()  .withIdentity("trigger7", "group1")  .withSchedule(simpleSchedule()                .withIntervalInMinutes(5)                .repeatForever())  .endAt(dateOf(22, 0, 0))  .build();

建设一个触发器,该触发器将在下一个小时的顶部触发,而后永远每2小时反复一次:

  trigger = newTrigger()    .withIdentity("trigger8") // because group is not specified, "trigger8" will be in the default group    .startAt(evenHourDate(null)) // get the next even-hour (minutes and seconds zero ("00:00"))    .withSchedule(simpleSchedule()        .withIntervalInHours(2)        .repeatForever())    // note that in this example, 'forJob(..)' is not called    //  - which is valid if the trigger is passed to the scheduler along with the job      .build();    scheduler.scheduleJob(trigger, job);

SimpleTrigger MisFire

MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICYMISFIRE_INSTRUCTION_FIRE_NOWMISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNTMISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNTMISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNTMISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT

6.CronTrigger

如果您须要一个基于相似于日历的概念而不是依据SimpleTrigger的确切距离反复产生的工作辞退时间表,则CronTrigger通常比SimpleTrigger有用。

应用CronTrigger,您能够指定触发打算,例如“每个星期五中午”,“每个工作日至9:30”,甚至“每个星期一,星期三的上午9:00至10:00之间的每5分钟”和一月的星期五”。

即便如此,与SimpleTrigger一样,CronTrigger具备一个startTime,用于指定打算何时失效;以及一个(可选的)endTime,用于指定打算何时终止。

Cron表达式

http://www.quartz-scheduler.o...

表达式用于配置CronTrigger的实例。Cron-Expression是实际上由七个子表达式组成的字符串,它们形容了日程表的各个细节。这些子表达式用空格分隔,代表:

  1. 分钟
  2. 小时
  3. 月的某一天
  4. 星期几
  5. 年(可选字段)
  • 残缺的cron表达式的示例是字符串 0 0 12 ?* WED

    • 代表每个星期三的12:00:00 pm”。

各个子表达式能够蕴含范畴和/或列表。

  • 例如,上一个WED字段能够替换为“ MON-FRI”,“ MON,WED,FRI”,甚至“ MON-WED,SAT”。

通配符''能够用于示意字段的所有可能值

*代表每一周的每一天

/代表指定值的增量。

  • 例如在分钟字段0/15,示意每个小时的15分钟,从0 分钟开始,
  • 如果用3/20示意每小时的第20分钟,从地三分钟开始
  • <span style='color:red'>留神</span>:/35并不代表每35分钟,而是每小时的每35分钟,从零分钟开始即0/35

?:星期几和星期几字段容许应用字符。用于指定“无特定值”。当您须要在两个字段之一中指定某项而不是另一个字段时,这很有用

L:用于月和周,示意最初一个,月示意月的最初一天1月31日,2月28日。用于星期的话示意7或者sat(周六),如果6L示意该月的最初一个最初一个星期五,能够用来指定该月最初一天的偏移量L-3,示意日历月的倒数第三天

W用来指定给定日期的工作日(周一到星期五),15W指的是离每月15日最近的工作日

用于指定每月的第“ n”个XXX工作日。例如,星期几字段中的 6#3 FRI#3的值示意每月的第三个星期五

Cron表达式示例

CronTrigger示例1-用于创立仅每5分钟触发一次的触发器的表达式

 0 0/5 * * *?

CronTrigger示例2-创立一个触发器的表达式,该触发器每5分钟触发一次,每分钟后10秒(例如10:00:10 am,10:05:10 am等)触发。

 10 0/5 * * * ?

CronTrigger示例3-创立一个触发器的表达式,该触发器在每个星期三和星期五的10:30、11:30、12:30和13:30触发。

 0 30 10-13 ?* WED,FRI

CronTrigger示例4-创立一个触发器的表达式,该触发器在每月的5号和20号的上午8点到10点之间每半小时触发一次。请留神,触发器不会在上午10:00,仅在8:00、8:30、9:00和9:30触发

0 0/30 8-9 5,20 * ?

请留神,某些打算要求太过简单而无奈用一次触发来表白,例如“上午9:00至上午10:00之间每5分钟一次,下午1:00至10:00下午每20分钟一次”。这种状况下的解决方案是简略地创立两个触发器,并注册两个触发器以运行雷同的作业。

构建CronTriggers

import static org.quartz.TriggerBuilder.*;import static org.quartz.CronScheduleBuilder.*;import static org.quartz.DateBuilder.*:

建设一个触发器,该触发器每天每天从早上8点到下午5点之间每隔一分钟触发一次:

trigger = newTrigger()  .withIdentity("trigger3", "group1")  .withSchedule(cronSchedule("0 0/2 8-17 * * ?"))  .forJob("myJob", "group1")  .build();

建设触发器,每天10:42 am触发

trigger = newTrigger()  .withIdentity("trigger3", "group1")  .withSchedule(dailyAtHourAndMinute(10, 42))  .forJob(myJobKey)  .build();

或者

trigger = newTrigger()  .withIdentity("trigger3", "group1")  .withSchedule(cronSchedule("0 42 10 * * ?"))  .forJob(myJobKey)  .build();

构建一个触发器,该触发器将在星期三上午10:42,应用零碎默认值以外的TimeZone触发

trigger = newTrigger()  .withIdentity("trigger3", "group1")  .withSchedule(weeklyOnDayAndHourAndMinute(DateBuilder.WEDNESDAY, 10, 42))  .forJob(myJobKey)  .inTimeZone(TimeZone.getTimeZone("America/Los_Angeles"))  .build();

或者

 trigger = newTrigger()    .withIdentity("trigger3", "group1")    .withSchedule(cronSchedule("0 42 10 ? * WED"))    .inTimeZone(TimeZone.getTimeZone("America/Los_Angeles"))    .forJob(myJobKey)    .build();

CronTrigger MisFire阐明

MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICYMISFIRE_INSTRUCTION_DO_NOTHINGMISFIRE_INSTRUCTION_FIRE_NOW
 trigger = newTrigger()    .withIdentity("trigger3", "group1")    .withSchedule(cronSchedule("0 0/2 8-17 * * ?")        ..withMisfireHandlingInstructionFireAndProceed())    .forJob("myJob", "group1")    .build();

7.TriggerListeners和JobListeners

侦听器是您创立的对象,用于依据调度程序中产生的事件执行操作。您可能会猜到,TriggerListeners*接管与触发器相干的事件,而JobListeners* 接管与作业相干的事件。

public interface TriggerListener {  public String getName();  public void triggerFired(Trigger trigger, JobExecutionContext context);  public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context);  public void triggerMisfired(Trigger trigger);  public void triggerComplete(Trigger trigger, JobExecutionContext context,                              int triggerInstructionCode);}

与作业相干的事件包含:行将执行作业的告诉,以及作业实现执行时的告诉。

public interface JobListener {  public String getName();  public void jobToBeExecuted(JobExecutionContext context);  public void jobExecutionVetoed(JobExecutionContext context);  public void jobWasExecuted(JobExecutionContext context,                             JobExecutionException jobException);}

应用本人的监听器

要创立侦听器,只需创立一个实现org.quartz.TriggerListener或org.quartz.JobListener接口的对象。而后,在运行时将侦听器注册到调度程序,并且必须给其指定名称(或者说,它们必须通过其getName()办法公布本人的名称)。

侦听器与调度程序的ListenerManager一起注册,该Matcher形容了侦听器要为其接管事件的作业/触发器。

侦听器在运行时会向调度程序注册,并且不会与作业和触发器一起存储在JobStore中。这是因为侦听器通常是您的应用程序的集成点。因而,每次您的利用程序运行时,都须要在调度程序中从新注册侦听器。
scheduler.getListenerManager().addJobListener(myJobListener, jobKeyEquals(jobKey("myJobName", "myJobGroup")));

增加对特定组的所有作业感兴趣的JobListener

scheduler.getListenerManager().addJobListener(myJobListener, jobGroupEquals("myJobGroup"));

增加对两个特定组的所有作业感兴趣的JobListener

scheduler.getListenerManager().addJobListener(myJobListener, or(jobGroupEquals("myJobGroup"), jobGroupEquals("yourGroup")));

增加对所有作业感兴趣的JobListener

scheduler.getListenerManager().addJobListener(myJobListener, allJobs());

Quartz的大多数用户都不应用侦听器,然而当应用程序需要创立事件告诉的需要时,侦听器十分不便,而Job自身不用显式告诉应用程序。

8.SchedulerListeners

*SchedulerListener*与TriggerListeners和JobListeners十分类似,除了它们在Scheduler本身内接管事件的告诉-不肯定与特定触发器或作业无关的事件。

与调度程序相干的事件包含:增加作业/触发器,移除作业/触发器,调度程序内的严重错误,告诉调度程序正在敞开等。

public interface SchedulerListener {  public void jobScheduled(Trigger trigger);  public void jobUnscheduled(String triggerName, String triggerGroup);  public void triggerFinalized(Trigger trigger);  public void triggersPaused(String triggerName, String triggerGroup);  public void triggersResumed(String triggerName, String triggerGroup);  public void jobsPaused(String jobName, String jobGroup);  public void jobsResumed(String jobName, String jobGroup);  public void schedulerError(String msg, SchedulerException cause);  public void schedulerStarted();  public void schedulerInStandbyMode();  public void schedulerShutdown();  public void schedulingDataCleared();}

增加一个SchedulerListener

scheduler.getListenerManager().addSchedulerListener(mySchedListener);

删除SchedulerListener

scheduler.getListenerManager().removeSchedulerListener(mySchedListener);

9.JobStore

JobStore负责跟踪您提供给调度程序的所有“JobData”:作业,触发器,日历等。为Quartz调度程序实例抉择适当的JobStore是重要的一步。

侥幸的是,一旦您理解了两者之间的差别,那么抉择就非常容易。

您在提供给用于生成调度程序实例的SchedulerFactory的属性文件(或对象)中申明调度程序应应用哪个JobStore(及其配置设置)。

切勿在代码中间接应用JobStore实例。因为某些起因,许多人试图这样做。JobStore用于Quartz自身的幕后应用。您必须(通过配置)通知Quartz应用哪个JobStore,然而您仅应在代码中应用Scheduler接口。

RAMJobStore

RAMJobStore是应用最简略的JobStore,也是性能最高的(就CPU工夫而言)。RAMJobStore的名称很显著:

  • 将所有数据保留在RAM中。这就是为什么它闪电般的疾速,也是为什么它是如此简略的配置。
  • 毛病是

    • 当您的应用程序完结(或解体)时,所有调度信息都将失落-
    • 这意味着RAMJobStore无奈承受JobTrigger上的duriable设置。
    • 对于某些应用程序,这是能够承受的,甚至是所需的行为,然而对于其余应用程序,这可能是灾难性的。

要应用RAMJobStore(并假如您正在应用StdSchedulerFactory),只需将类名称org.quartz.simpl.RAMJobStore指定为用于配置石英的JobStore类属性:

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

JDBCJobStore

JDBCJobStore也被失当地命名-它通过JDBC将其所有数据保留在数据库中。因而,它的配置要比RAMJobStore简单一些,并且速度也没有那么快。

然而,性能降落并不是很蹩脚,尤其是当您应用主键上的索引构建数据库表时。在具备适当LAN(在调度程序和数据库之间)的相当古代的一组计算机上,检索和更新触发触发器的工夫通常少于10毫秒

JDBCJobStore简直能够与任何数据库一起应用,它已被Oracle,PostgreSQL,MySQL,MS SQLServer,HSQLDB和DB2宽泛应用。要应用JDBCJobStore,必须首先创立一组数据库表供Quartz应用。您能够在Quartz发行版的 docs / dbTables目录中找到表创立SQL脚本。如果没有针对您的数据库类型的脚本,只需查看现有脚本之一,而后以数据库所需的任何形式对其进行批改。须要留神的一件事是,在这些脚本中,所有表都以前缀“ QRTZ_”结尾(例如表“ QRTZ_TRIGGERS”和“ QRTZ_JOB_DETAIL”)。只有您告知JDBCJobStore前缀是什么(在Quartz属性中),该前缀实际上就能够是您想要的任何前缀。应用不同的前缀对于创立多个表集,多个调度程序实例可能很有用,

创立表之后,在配置和启动JDBCJobStore之前,您须要做出另一个重要决定。您须要确定您的应用程序须要哪种事物来治理。如果您不须要将调度命令(例如增加和删除触发器)与其余事务绑定,则能够让Quartz通过将JobStoreTX用作JobStore来治理事务(这是最常见的抉择)。

如果您须要Quartz与其余事务一起工作(即在J2EE应用程序服务器中),则应应用JobStoreCMT-在这种状况下,Quartz将容许应用程序服务器容器治理事务。

最初一个难题是设置一个数据源,JDBCJobStore能够从该数据源取得与您的数据库的连贯。

数据源是应用几种不同办法之一在Quartz属性中定义的。

  • 一种办法是让Quartz通过提供数据库的所有连贯信息来创立和治理DataSource自身。
  • 另一种办法是通过提供JDBCJobStore数据源的JNDI名称,使Quartz应用由Quartz在其中运行的应用程序服务器治理的数据源。无关属性的详细信息,请查阅docs / config文件夹中的示例配置文件。

要应用JDBCJobStore(并假如您应用的是StdSchedulerFactory),首先须要将Quartz配置的JobStore类属性设置为org.quartz.impl.jdbcjobstore.JobStoreTXorg.quartz.impl.jdbcjobstore.JobStoreCMT依据以上几段中的阐明进行的抉择。

配置Quartz以应用JobStoreTx

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

接下来,您须要抉择一个DriverDelegate供JobStore应用。DriverDelegate负责实现特定数据库可能须要的任何JDBC工作。

StdJDBCDelegate是应用“原始” JDBC代码(和SQL语句)实现其工作的委托。如果没有专门为您的数据库创立的另一个委托,请尝试应用此委托-咱们仅对应用StdJDBCDelegate与(发现最多!)发现问题的数据库进行了特定于数据库的委托。其余代表能够在“ org.quartz.impl.jdbcjobstore”包或其子包中找到。其余代表包含DB2v6Delegate(用于DB2版本6和更早版本),HSQLDBDelegate(用于HSQLDB),MSSQLDelegate(用于Microsoft SQLServer),PostgreSQLDelegate(用于PostgreSQL),WeblogicDelegate(用于应用由Weblogic制作的JDBC驱动程序),

抉择委托后,将其类名称设置为JDBCJobStore应用的委托。

配置JDBCJobStore以应用DriverDelegate

org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate

接下来,您须要告诉JobStore您正在应用的表前缀(下面已探讨过)。

应用表前缀配置JDBCJobStore

org.quartz.jobStore.tablePrefix = QRTZ_

最初,您须要设置JobStore应该应用哪个数据源。还必须在Quartz属性中定义命名的DataSource。在这种状况下,咱们指定Quartz应该应用数据源名称“ myDS”(在配置属性的其余地位定义)。

应用要应用的数据源的名称配置JDBCJobStore

org.quartz.jobStore.dataSource = myDS
如果您的调度程序很忙(即简直总是执行与线程池大小雷同的作业数),那么您可能应该将DataSource中的连接数设置为线程池大小的+ 2。

能够将“ org.quartz.jobStore.useProperty”配置参数设置为“ true”(默认为false),以批示JDBCJobStore JobDataMaps中的所有值均为字符串,因而能够存储为名称-值对,而不是而不是将更简单的对象以其序列化模式存储在BLOB列中。从久远来看,这样做更加平安,因为能够防止将非String类序列化为BLOB时呈现的类版本控制问题。

TerracottaJobStore

TerracottaJobStore提供了一种无需应用数据库即可进行缩放和加强性能的办法。这意味着您的数据库能够免于Quartz的负载,而能够为应用程序的其余部分保留所有资源。

TerracottaJobStore能够集群化或非集群化运行,并且在两种状况下都能够为您的作业数据提供一种存储介质,该存储介质在应用程序重新启动之间是长久的,因为数据存储在Terracotta服务器中。它的性能比通过JDBCJobStore应用数据库要好得多(大概好一个数量级),但比RAMJobStore慢得多。

要应用TerracottaJobStore(并假如您应用的是StdSchedulerFactory),只需指定类名称org.quartz.jobStore.class = org.terracotta.quartz.TerracottaJobStore作为用于配置Quartz的JobStore类属性,而后增加一行配置以指定Terracotta服务器的地位:

org.quartz.jobStore.class = org.terracotta.quartz.TerracottaJobStoreorg.quartz.jobStore.tcConfigUrl = localhost:9510

无关此JobStore和Terracotta的更多信息, 请拜访http://www.terracotta.org/quartz。

10.配置,资源应用状况和SchedulerFactory

Quartz的体系结构是模块化的,因而要使其运行,须要将多个组件“绑定”在一起。侥幸的是,存在一些帮忙实现此指标的助手。

Quartz进行工作之前须要配置的次要组件是:

  • 线程池
  • 作业库
  • 数据源
  • 调度程序自身

*线程池*提供了一组线程供Quartz在执行Jobs时应用。池中的线程越多,能够并行运行的作业数越多。然而,太多线程可能会使您的零碎瘫痪。

大多数Quartz用户发现5个左右的线程是足够的-因为在任何给定的工夫它们少于100个作业,通常不安顿这些作业同时运行,并且这些作业是短暂的(疾速实现)。

其余用户发现他们须要10、15、50甚至100个线程-因为它们具备成千上万个具备各种打算的触发器-最终均匀有10到100个试图在任何给定时刻执行的作业。

为调度程序池找到适合的大小齐全取决于您应用调度程序的目标。没有真正的规定,除了使线程数尽可能小(为了节俭计算机资源)外-还要确保有足够的空间按时启动作业。

请留神,如果触发器的触发工夫到了,并且没有可用的线程,Quartz将阻塞(暂停)直到某个线程可用,而后作业将执行-比原先的工夫晚了几毫秒。

如果在调度程序配置的misfire阈值期间没有可用的线程,这甚至可能导致线程不触发。

在org.quartz.spi包中定义了ThreadPool接口,您能够依照本人喜爱的任何形式创立ThreadPool实现。Quartz附带了一个简略(但十分令人满意)的线程池,名为org.quartz.simpl.SimpleThreadPool。此ThreadPool只是在其池中保护一组固定的线程-永不增长,永不膨胀。然而它十分强壮,并且通过了很好的测试-因为简直所有应用Quartz的人都应用该池。

这里值得一提的是,所有JobStore都实现了org.quartz.spi.JobStore接口-如果捆绑的JobStore之一不合乎您的需要,那么您能够本人制作。

最初,您须要创立Scheduler实例。须要给Scheduler自身一个名称,通知它的RMI设置,并传递JobStore和ThreadPool的实例。RMI设置包含调度程序是否应将其本身创立为RMI的服务器对象(使其可用于近程连贯),要应用的主机和端口等。StdSchedulerFactory(上面探讨)还能够产生实际上是代理的Scheduler实例( RMI存根)到在近程过程中创立的调度程序。

StdSchedulerFactory

StdSchedulerFactory是org.quartz.SchedulerFactory接口的实现。它应用一组属性(java.util.Properties)创立和初始化Quartz Scheduler。

这些属性通常存储在文件中并从文件中加载,然而也能够由程序创立并间接交给工厂。只需在工厂上调用getScheduler()即可生成调度程序,对其进行初始化(及其ThreadPool,JobStore和DataSources),并将句柄返回其公共接口。

Quartz发行版的“ docs / config”目录中有一些示例配置(包含属性阐明)。您能够在Quartz文档的“Reference”局部下的“Configuration”手册中找到残缺的文档。

DirectSchedulerFactory

DirectSchedulerFactory是另一个SchedulerFactory实现。对于心愿以更具编程性的形式创立其Scheduler实例的用户来说,这很有用。通常不倡议应用它,起因如下:

  • 它要求用户对他们的工作有更深刻的理解
  • 它不容许进行申明式配置-换句话说,您最终会很难-对所有调度程序的设置进行编码。

日志

Quartz应用SLF4J框架来满足其所有日志记录需要。为了“调整”日志记录设置(例如输入的数量以及输入的输入地位),您须要理解SLF4J框架,这不在本文档的探讨范畴之内。

如果要捕捉无关触发器触发和作业执行的其余信息,则可能对启用org.quartz.plugins.history.LoggingJobHistoryPluginorg.quartz.plugins.history.LoggingTriggerHistoryPlugin感兴趣。

11.高级性能

群集以后可与JDBC-Jobstore(JobStoreTX或JobStoreCMT)和TerracottaJobStore一起应用。性能包含负载平衡和作业故障转移(如果JobDetail的“申请复原”标记设置为true)。

应用JobStoreTX或JobStoreCMT进行群集通过将 org.quartz.jobStore.isClustered属性设置为“ true”来启用群集。

集群中的每个实例都应应用quartz.properties文件的雷同正本。例外情况是应用雷同的属性文件,但容许以下例外:不同的线程池大小和org.quartz.scheduler.instanceId属性的不同值。

集群中的每个节点必须具备惟一的instanceId,能够通过将“ AUTO”搁置为该属性的值来轻松实现(不须要其余属性文件)。

切勿在独自的计算机上运行集群,除非应用十分定期运行的某种模式的工夫同步服务(守护程序)同步它们的时钟(时钟之间的工夫距离必须在1秒钟之内)。 如果您不相熟此操作,请参阅http://www.boulder.nist.gov/t...。

切勿针对其余实例正在运行的同一组表启动非集群实例。您可能会遇到重大的数据损坏,并且必定会遇到不稳固的行为

每次触发时,只有一个节点将触发该作业。我的意思是,如果作业具备反复的触发器,通知它每10秒触发一次,则在12:00:00恰好一个节点将运行该作业,而在12:00:10恰好一个节点将运行作业等等。不肯定每次都在同一个节点上-哪个节点运行它或多或少是随机的。对于忙碌的调度程序(大量触发器),负载平衡机制简直是随机的,但偏差于对于非忙碌的调度程序(例如,一个或两个触发器)仅处于活动状态的同一节点。

应用TerracottaJobStore进行集群只需配置调度程序以应用TerracottaJobStore(已在 第9课:JobStores中介绍),您的调度程序将全副设置为集群。

您可能还须要思考如何设置Terracotta服务器的含意,特地是关上诸如持久性等性能以及为HA运行一系列Terracotta服务器的配置选项。

TerracottaJobStore企业版提供高级Quartz Where性能,可将作业智能地定向到适当的群集节点。

JTA事物

JobStoreCMT容许在较大的JTA事务中执行Quartz调度操作。

通过将org.quartz.scheduler.wrapJobExecutionInUserTransaction属性设置为 true,作业还能够在JTA事务(UserTransaction)中执行。

设置此选项后,JTA事务将在Job的execute办法被调用之前开始begin(),而execute调用终止后将进行commit()。这实用于所有作业。

如果要为每个作业批示JTA事务是否应该包装其执行,则应在作业类上应用 @ExecuteInJTATransaction批注。

除了Quartz在JTA事务中主动包装Job执行之外,在应用JobStoreCMT时,您在Scheduler接口上进行的调用也会参加事务。只需确保已启动事务,而后再调用调度程序上的办法即可。您能够通过应用UserTransaction来间接执行此操作,也能够将应用调度程序的代码放在应用容器治理的事务的SessionBean中。

12.其余性能

Quartz提供了一个用于插入附加性能的接口(org.quartz.spi.SchedulerPlugin)。

能够在*org.quartz.plugins* 包中找到Quartz附带的提供各种实用程序性能的插件。它们提供了一些性能,例如在调度程序启动时主动调度作业,记录作业和触发事件的历史记录,并确保在JVM退出时调度程序齐全敞开。

JobFactory

触发触发器时,将通过在Scheduler上配置的JobFactory实例化与之关联的Job。默认的JobFactory仅在作业类上调用newInstance()。

您可能须要创立本人的JobFactory实现,以实现诸如使应用程序的IoC或DI容器生成/初始化作业实例之类的事件。

Factory-Shipped Jobs

Quartz还提供了许多实用程序作业,您能够在应用程序中应用它们来实现诸如发送电子邮件和调用EJB之类的事件。这些开箱即用的作业能够在*org.quartz.jobs* 包中找到。