quartz - 基础知识

1. Quartz 设计模式

  • Bulider模式  
  • Factory模式
  • 组件模式
  • 链式编程

2. Quartz两种执行模式

  RAMJobStore :

  RAMJobStore是默认的数据存储方式,其把数据存在本地内存中,官方宣称这是最有效率的方式(在本地内存当然快啦)。但是在宕机或者重启的时候数据就会丢失。

 

  JDBCJobStore:

  JDBCJobStore终于把数据给持久化起来了,这个也是使用最广泛的数据存储方式。在于Spring集成后都不需要自己再配置一遍数据库连接了。建表脚本在官方包里面可以找到(http://www.quartz-scheduler.org/downloads/)

 

3. Quartz核心概念(通过quartz工厂获取这三个组件)

  * 调度器Scheduler

  Scheduler将任务Job和触发器Trigger整合起来,负责基于Trigger设定的时间去执行任务Job;

  Scheduler作为我们的“任务记录表”,里面(可以)配置大量的Trigger和JobDetail,两者在 Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。

  SchedulerFactory schedulerFactory = new StdSchedulerFactory();
  Scheduler scheduler = schedulerFactory.getScheduler();//将jobDetail和Trigger注册到一个scheduler里,建立起两者的关联关系
  scheduler.scheduleJob(jobDetail,Trigger);
  scheduler.start();//开始任务调度

 

  * 任务Job

  job就是你想要实现的任务类,每一个job必须实现org.quartz.job接口,且只需时间接口定义的execu()方法;

public class IapetoseeJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobKey key = context.getJobDetail().getKey();
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();    
        String myString= dataMap.getString("我的字符串");
        int myInt = dataMap.getInteger("我的数字");
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        System.out.println("在"+sdf.format(new Date())+"扒取新闻");
    }
}

 

  * JobDetail补充

  Quartz在每次执行任务时,都会创建一个Job实例,并为其配置上下文信息,jobDetail有一个成员属性JobDataMap,存放了我们Job运行时的具体信息,在后面我们会详细提到。 
  1. 在1.+版本中,它作为一个类,常用的构造方法有:JobDetail(String name, String group, Class jobClass),我们需要指定job的名称,组别和实现了Job接口的自定义任务类。实例如JobDetail jobDetail =new JobDetail("job1", "jgroup1", pickNewsJob.class); 
  2. 而在2.+版本中,我们则通过一下方法创建 JobBuilder.newJob(自定义任务类).withIdentity(任务名称,组名).build();实例如JobDetail jobDetail = JobBuilder.newJob(pickNewsJob.class).withIdentity(“job1”,”group1”).build();`

  JobDetail job = newJob(IapetoseeJob.class)
      .withIdentity("任务名称", "组名") 
      .usingJobData("我的字符串", "Hello World!").usingJobData("我的数字",12341234)
      .build();

    * 触发器Trigger

  Trigger描述了Job执行的时间触发规则,主要有SimpleTrigger和CronTrigger两个子类。

  SimpleTrigger simpleTrigger = TriggerBuilde.newTrigger() 
.withIdentity("trigger1")//配置触发器名称 .withSchedule(SimpleScheduleBuilder.repeatSecondlyForTotalCount(10, 2))//配置重复次数和间隔时间 .startNow()//设置从当前开始 .build();//创建操作

 4. 简单实现过程

  public static void main(String[] args) throws SchedulerException {
        //获得调度器
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        //创建任务实例
        JobDetail jobDetail = JobBuilder.newJob(QiyeJob.class).withIdentity("qiye", "gongshang").build();
        //创建任务执行条件
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("qiyeTrigger", "gongshangqiyeTrigger").withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).startNow().build();
        //关联任务
        scheduler.scheduleJob(jobDetail,trigger);
        //执行任务
        scheduler.start();
    }

 5. 有状态的job

  Quartz,每次执行job,job永远是全新的对象,但是,如果job实现org.quartz.StatefulJob接口,而不是job接口.

  此时JobDetail的JobDataMap将会共享一个对象。
  注意:
    当实现有状态接口,StatefulJob时,只有JobDetail的JobDataMap是共用的,其他的,比如,Job本身,Trigger等,仍然每次执行 的时候是全新的对象。所以,只有JobDetail的JobDataMap是共用的,其他的trigger.getJobDataMap(),context.getMergedJobDataMap(),等这些JobDataMap,任然是全新的
6. StdSchedulerFactory和DirectSchedulerFactory的区别

  StdSchedulerFactory

  StdSchedulerFactory是对org.quartz.SchedulerFactory接口的一个实现。是使用一套属性(NameValueCollection)来创建和初始化Quartz Scheduler。这些属性通常在文件中存储和加载。也可以通过编写程序来直接操作工厂。简单地调用工厂的getScheduler()就可以产生一个scheduler,初始化(以及它的ThreadPool、JobStore和DataSources),并且返回一个公共的接口。

  DirectSchedulerFactory

  DirectSchedulerFactory是SchedulerFactory的另一个实现。它对于那些希望用更加程序化的方式创建Scheduler非常有用。不鼓励使用它的原因如下:

  (1) 它需要用户非常了解他们想要干什么。

  (2) 它不允许声明式的配置。换句话说,它使用硬编码的方式设置scheduler。

7. Quartz监听器

  Quartz的监听器用于当任务调度中你所关注事件发生时,能够及时获取这一事件的通知。类似于任务执行过程中的邮件、短信类的提醒。Quartz监听器主要有JobListener、TriggerListener、SchedulerListener三种,顾名思义,分别表示任务、触发器、调度器对应的监听器。三者的使用方法类似,在开始介绍三种监听器之前,需要明确两个概念:全局监听器与非全局监听器,二者的区别在于:全局监听器能够接收到所有的Job/Trigger的事件通知,而非全局监听器只能接收到在其上注册的Job或Trigger的事件,不在其上注册的Job或Trigger则不会进行监听。

  (1)JobListener

    a.  getName方法:用于获取该JobListener的名称。

    b.  jobToBeExecuted方法:Scheduler在JobDetail将要被执行时调用这个方法。

    c.  jobExecutionVetoed方法:Scheduler在JobDetail即将被执行,但又被TriggerListerner否决时会调用该方法

    d.  jobWasExecuted方法:Scheduler在JobDetail被执行之后调用这个方法

public class SystemJoblistener implements JobListener {

    @Override
    public String getName() {
        //输出当前监听器的名字
        return this.getClass().getSimpleName();
    }


    @Override
    public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {
        //在任务执行之前
        String name = jobExecutionContext.getJobDetail().getKey().getName();
        System.out.println("1 任务:"+name+"执行了");

    }

    @Override
    public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
        //在任务执行被否决的时候
        System.out.println("2 Scheduler在JobDetail即将被执行,但又被TriggerListerner否决时会调用该方法");
    }

    @Override
    public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
        //在任务执行完成之后执行
        String name = jobExecutionContext.getJobDetail().getKey().getName();
        System.out.println("3 任务:"+name+"执行完了");
    }
}

  注册进scheduler中

    //设置全局job监听器
        ListenerManager listenerManager = scheduler.getListenerManager();
        listenerManager.addJobListener(new SystemJoblistener(), EverythingMatcher.allJobs());

  

  (2)TriggerListener

方法说明
getName() 定义并返回监听器的名字
triggerFired() 当与监听器相关联的 Trigger 被触发,Job 上的 execute() 方法将要被执行时,Scheduler 就调用这个方法。在全局 TriggerListener 情况下,这个方法为所有 Trigger 被调用。
vetoJobExecution() 在 Trigger 触发后,Job 将要被执行时由 Scheduler 调用这个方法。TriggerListener 给了一个选择去否决 Job 的执行。假如这个方法返回 true,这个 Job 将不会为此次 Trigger 触发而得到执行。
triggerMisfired() Scheduler 调用这个方法是在 Trigger 错过触发时。如这个方法的 JavaDoc 所指出的,你应该关注此方法中持续时间长的逻辑:在出现许多错过触发的 Trigger 时,长逻辑会导致骨牌效应。你应当保持这上方法尽量的小。
triggerComplete() Trigger 被触发并且完成了 Job 的执行时,Scheduler 调用这个方法。这不是说这个 Trigger 将不再触发了,而仅仅是当前 Trigger 的触发(并且紧接着的 Job 执行) 结束时。这个 Trigger 也许还要在将来触发多次的。
 
public class MyTriggerListener implements TriggerListener {

    @Override
    public String getName() {
        return "MyOtherTriggerListener";//这个不能返回null,必须填写;可以使用类的构造器传参的方法传入其他的Trigger监听器名称
    }

    /**
     * (1)
     * Trigger被激发 它关联的job即将被运行
     * Called by the Scheduler when a Trigger has fired, and it's associated JobDetail is about to be executed.
     */
    @Override
    public void triggerFired(Trigger trigger, JobExecutionContext context) {
        System.out.println("MyOtherTriggerListener.triggerFired()");
    }

    /**
     * (2)
     * Trigger被激发 它关联的job即将被运行,先执行(1),在执行(2) 如果返回TRUE 那么任务job会被终止
     * Called by the Scheduler when a Trigger has fired, and it's associated JobDetail is about to be executed
     */
    @Override
    public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
        System.out.println("MyOtherTriggerListener.vetoJobExecution()");
        return false;
    }

    /**
     * (3) 当Trigger错过被激发时执行,比如当前时间有很多触发器都需要执行,但是线程池中的有效线程都在工作,
     *  那么有的触发器就有可能超时,错过这一轮的触发。
     * Called by the Scheduler when a Trigger has misfired.
     */
    @Override
    public void triggerMisfired(Trigger trigger) {
        System.out.println("MyOtherTriggerListener.triggerMisfired()");
    }

    /**
     * (4) 任务完成时触发
     * Called by the Scheduler when a Trigger has fired, it's associated JobDetail has been executed
     * and it's triggered(xx) method has been called.
     */
    @Override
    public void triggerComplete(Trigger trigger, JobExecutionContext context,
            CompletedExecutionInstruction triggerInstructionCode) {
        System.out.println("MyOtherTriggerListener.triggerComplete()");
    }

}

  

//向调度程序注册全局监听触发器

scheduler.getListenerManager().addTriggerListener(myTriggerListener, allTriggers());

//向调度程序注册一个指定的监听触发器

scheduler.getListenerManager().addTriggerListener(myTriggerListener, triggerKeyEquals(triggerKey("myTriggerName", "myTriggerGroup")));

//向调度程序注册一个特定组的触发器
scheduler.getListenerManager().addTriggerListener(myTriggerListener, triggerGroupEquals("myTriggerGroup"));

  

  (3)SchedulerListener

posted @ 2019-05-18 16:16  iapetosee  阅读(431)  评论(0编辑  收藏  举报