`
当代阿Q
  • 浏览: 62816 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

企业级任务调度框架Quartz 九 Quartz之作业触发器Trigger

阅读更多
前序:
     我们已经大概对Quartz的基本有了一个大概的认识;现在我们将要逐渐对Quartz的各个重要组件进行学习;前面已经对job进行了详细讲解,现在我们来认识下它的一个重要兄弟,没有它,作业将无法得到执行的---触发器Trigger;

1.Quartz中的触发器Trigger
  Job 包含了要执行任务的逻辑,但是 Job 对何时该执行却一无所知。这个事情留给了 Trigger。Quartz Trigger 继承了抽象的 org.quartz.Trigger 类。当前,Quartz 有三个可用的 Trigger:
    
    ·org.quartz.SimpleTrigger

    ·org.quartz.CronTrigger

    ·org.quartz.NthIncludeDayTrigger


2.SimpleTrigger的介绍
   正如其名所示,SimpleTrigger对于设置和使用是最为简单的一种 Quartz Trigger。它是为那种需要在特定的日期/时间启动,且以一个可能的间隔时间重复执行 n  次的 Job 所设计的。
   我们前面已经在一个简单的Quartz的例子里使用过了SimpleTrigger,我们通过
  
Trigger trigger = TriggerUtils.makeSecondlyTrigger(10);

来获取到我们的Trigger,而得到的实际上就是SimpleTrigger类的一个实现;我们实际上
还可以对上面的代码进行小小的改造:
/*  
 * Create a SimpleTrigger that starts immediately,  
 * with a null end date/time, repeats forever and has  
 * 1 minute (60000 ms) between each firing.  
 */  
Trigger trigger =
new SimpleTrigger("myTrigger",Scheduler.DEFAULT_GROUP,
new Date(),null,SimpleTrigger.REPEAT_INDEFINITELY,60000L); 

对于Quartz而言,它还不能满足我们的触发情况,所以它仅仅是用于一些简单的触发情况;

3.org.quartz.CronTrigger
  CronTrigger 允许设定非常复杂的触发时间表。然而有时也许不得不使用两个或多个 SimpleTrigger 来满足你的触发需求,这时候,你仅仅需要一个 CronTrigger 实例就够了。顾名思义,CronTrigger 是基于 Unix 类似于 cron 的表达式。例如,你也许有一个 Job,要它在星期一和星期五的上午 8:00-9:00 间每五分钟执行一次。假如你试图用 SimpleTrigger 来实现,你或许要为这个 Job 配置多个 Trigger。然而,你可以使用如下的表达式来产生一个遵照这个时间表触发的 Trigger;
比如:
try {   
     CronTrigger cTrigger = new CronTrigger("myTrigger",   
               Scheduler.DEFAULT_GROUP, "0 0/5 8 ? *  
MON,FRI");   
} catch (ParseException ex) {   
ex.printStackTrace();   
}  

因为 CronTrigger 内建的如此强的灵活性,也与生俱来可用于创建几乎无所限制的表达式,且因为支持unix的cron表达式,则做为企业应用,我们的操作系统一般也都以unxi操作系统为主,所以掌握CronTrigger 的使用费用有必要,我们将在后面对CronTrigger 进行详细的介绍;

4.org.quartz.NthIncludedDayTrigger
   org.quartz.NthIncludedDayTrigger 是 Quartz 开发团队最新加入到框架中的一个 Trigger。它设计用于在每一间隔类型的第几天执行 Job。例如,你要在每个月的 15 号执行开票的 Job,用 NthIncludedDayTrigger就再合适不过了。Quartz的 Caldendar 也可与 Trigger 关联以此把周末与节假日考虑进来,并在必要时跳开这些日期。接下来的代码片断描绘了如何创建一个 NthIncludedDayTrigger.
NthIncludedDayTrigger trigger =   
new NthIncludedDayTrigger("MyTrigger",Scheduler.DEFAULT_GROUP);   
trigger.setN(15);   
trigger.setIntervalType(NthIncludedDayTrigger.INTERVAL_TYPE_MONTHLY); 


5.Job与Trigger的关系
   大家都知道,一个作业,比较重要的三个要素就是Schduler,jobDetail,Trigger;
而Trigger对于job而言就好比一个驱动器;没有触发器来定时驱动作业,作业就无法运行;
对于Job而言,一个job可以对应多个Trigger,但对于Trigger而言,一个Trigger只能对应一个job;所以一个 Trigger 只能被指派给一个 Job;
   如果你需要一个更复杂的触发计划,你可以创建多个 Trigger 并指派它们给同一个 Job。Scheduler 是基于配置在 Job 上的 Trigger 来决定正确的执行计划的,下面就是为同一个 JobDetail 使用多个Trigger;
try {   
// 创建一个任务调度器
Scheduler scheduler=StdSchedulerFactory.getDefaultScheduler();   
scheduler.start();   
logger.info("Scheduler has been started");  
//为一个作业类创建一个作业明细 
JobDetail jobDetail =new JobDetail("PrintInfoJob",Scheduler.DEFAULT_GROUP,PrintInfoJob.class); 
//省略为jobDetail设置JobDataMap的代码  
//创建一个触发器1,每5S触发一次
Trigger trigger1 =TriggerUtils.makeSecondlyTrigger("trigger1",5000, SimpleTrigger.REPEAT_INDEFINITELY);   
//创建一个触发器1,每10S触发一次  
Trigger trigger2 =TriggerUtils.makeMinutelyTrigger("trigger2", 10,   
SimpleTrigger.REPEAT_INDEFINITELY);   
//将作业在任务调度器上进行注册
scheduler.scheduleJob(jobDetail,trigger1);   
scheduler.scheduleJob(jobDetail, trigger2);    
} catch (SchedulerException ex) {   
  logger.error(ex);   
}

我们可以看出了,将一个作业注册到任务调度器上,并非以Job来进行识别,而是作业对应的触发器;也就是可以理解为,在该任务调度器上,如果你这个作业任务可能在不同的时间点被启动多次,且并非周期性的(即触发点有多个),则你需要在该任务调度器上注册多次;

6.Trigger的另一个重要内容--Calendar
   Calendar在java里,它的概念目前做为日历类来进行使用,并且多用于日期类的计算和处理;但我们在Quartz里的calendar则不是这个概念,而Quartz 的 Calender 专门用于屏闭一个时间区间,使 Trigger 在这个区间中不被触发;
   
    ·让你的 Job 总是运行。(这会让银行一团糟)

    ·在节假日期间手动停止你的 Job。(需要专门的人来负责做这个事情)

    ·创建不包含这些日子的多个 Trigger。(这对于设置和维护会较耗时的)

    ·设立一个排除这些日子的银行节日的 Calendar。(很轻松的实现)

    我们可以认为它就是一个屏蔽区域,例如,让我们假如你是为一个财务机构(如银行)工作。对于银行普遍的都有许多 "银行节日"。假设你不需要(或不想) Job 在那些日子里运行。那么只要设置一个这样的Calendar就可以了,这样就可以实现指定时间里,这些作业不会周期的执行了;
    Quartz的Calendar接口,有许多实现,它们可以帮助我们应对各种隔离区间的表示,下面我们就来仔细认识下它们;

    org.quartz.Calender 接口
public interface Calendar extends java.io.Serializable {
    /*
     *接口中定义的隔离区间是以月份为单位,即某月的某段日期
      */
    int MONTH = 0;
    /**
     * <p>
     * 设置一个新的calendar实例,或删除掉旧的calendar实例
     * </p>
     */
    void setBaseCalendar(Calendar baseCalendar);

    /**
     * <p>
     * 获得到当前的calendar实例
     * </p>
     */
    Calendar getBaseCalendar();

    /**
     * <p>
     * 设定屏蔽作业启动的时间;
     * </p>
     */
    boolean isTimeIncluded(long timeStamp);

    /**
     * <p>
     *  根据指定的时间,获取到下一次屏蔽区域的时间,比如今天做为作业
      *  作业屏蔽的时间,则按照指定屏蔽的规则,我们就可以知道下一次的屏蔽时间;
     * </p>
     */
    long getNextIncludedTime(long timeStamp);

    /**
     * <p>
     * 获取当前Calendar对应的描述信息;
      * </p>
     */
    String getDescription();

    /**
     * <p>
     * 当前Calendar对应的描述信息,可以设置它来说明设置当前Calendar的
      * 设置目的和意义;
     * </p>
     */
    void setDescription(String description);
}

下面是Quartz提供给用户可以使用的已实现Calendar类;Calender
BaseCalender
org.quartz.impl.calendar.BaseCalender为高级的 Calender 实现了基本的功能,实现了 org.quartz.Calender 接口

WeeklyCalendar
org.quartz.impl.calendar.WeeklyCalendar排除星期中的一天或多天,例如,可用于排除周末

MonthlyCalendar
org.quartz.impl.calendar.MonthlyCalendar排除月份中的数天,例如,可用于排除每月的最后一天

AnnualCalendar
org.quartz.impl.calendar.AnnualCalendar排除年中一天或多天

HolidayCalendar
org.quartz.impl.calendar.HolidayCalendar特别的用于从 Trigger 中排除节假日


大家请注意:
  对于Calendar,它可以为多个job服务,也就是它可以做为一个公共的信息,与多个Trigger进行绑定;而并不与Trigger一样,只能对应一个Job;

Calendar的使用方式如下:
   要使用 Quartz Calendar,你只需简单的实例化,并加入你要排除的日期,然后用 Scheduler 注册它。最后把这个 Calender 实例与你想要使用该 Calender 的每一个 Trigger 实例关联起来。

a.使用WeeklyCalendar
    排除星期中的一天或多天,例如,可用于排除周末;我们假定每周日做为休息日期,不执行某作业的执行;
    下面是它的应用
    LogUtil.print("将作业注册到调度器上");
    Scheduler scheduler=this.getScheduler();
    String dirPath = "E:\\重要";
    String matchName = ".rar";
    //设置JobDataMap
    JobDataMap jobDataMap=new JobDataMap();
    jobDataMap.put("SCAN_DIR", dirPath);
    jobDataMap.put("MATCH_NAME", matchName);
    //设置JobDetail 
    JobDetail jobDetail = new JobDetail  
    ("ScanDirectory",scheduler.DEFAULT_GROUP, ScanDirectoryJob.class);
    jobDetail.setJobDataMap(jobDataMap);
    
    //创建以周做为周期的触发器
     WeeklyCalendar cal = new WeeklyCalendar();    
     boolean[] excludeDays = new boolean[8];
     excludeDays[1]=true; //周日
      excludeDays[2]=false;//周1
     excludeDays[3]=false;//周2
     excludeDays[4]=false;//周3
     excludeDays[5]=false;//周4
     excludeDays[6]=false;//周5
     excludeDays[7]=true; //周6
     cal.setDaysExcluded(excludeDays);

     //将触发器对应的Calendar注册在任务调度器上
     scheduler.addCalendar("bankHolidays", cal, true, true);   
     Trigger trigger =
     TriggerUtils.makeImmediateTrigger("myTrigger",-1,6000);   
     trigger.setCalendarName("bankHolidays");
     //注册作业到任务调度器(作业明细与触发器对象)
     scheduler.scheduleJob(jobDetail, trigger);    


在上面的代码中,当我们在周末执行该文件扫描任务的时候,任务将不会被执行;而当起他时间点的时候,该任务才会被执行;

b.使用MonthlyCalendar



 




分享到:
评论
1 楼 penncy 2013-06-20  
你好,想问一下你关于Quartz里面关于misfire的操作的类型的详细解释。。。。
我们现在在做一个消息系统,可创建任务并根据任务指明的时间段发给全世界不同的国家和地区。
因为各个地区时间有时差的关系,我们将所有Timezone中每一个timezone创建一个task给quartz scheduler,然后在该时间段内触发后发送。所以当消息发送器忙的时候,有些任务应该是misfire了(配置工作的线程池大小为1)。
我们期待的结果是:如果misfire了,如果还在该允许发送的时间段以内,应该立即执行,否则应该推迟到第二天发送。
使用的trigger builder:DailyTimeIntervalScheduleBuilder
尝试的misfire instruction:
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW: 只会执行一次,而我配置的是一直重复执行,直到我逻辑判断结束后终止或者过了当日派发的时间。而且如果其他任务忙的时候,不应该强制触发。
MISFIRE_INSTRUCTION_DO_NOTHING: 忽视策略,什么都不做,而且之后都不会再执行了。等于说,如果一旦misfire之后,以后都不会再触发。
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY: misfire的那些,会在过了应该派发的时段,为弥补之前的缺漏,而加速触发从而赶上之前的触发次数。甚至在过了task配置的当日结束时间之后也触发!而且速度不可控造成系统资源占用过大。

综上之后,可配置的都不可用。。。难道只有我自己写一个触发回调来处理所有逻辑?这样要写很多的东西来处理quartz的接口。。。

期待您给予指点。谢谢!

相关推荐

Global site tag (gtag.js) - Google Analytics