Quartz实现与组件监听

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。Quartz是一个完全由java编写的开源作业调度框架。不要让作业调度这个术语吓着你。尽管Quartz框架整合了许多额外功能, 但就其简易形式看,你会发现它易用得简直让人受不了!。Quartz框架的核心是调度器。调度器负责管理Quartz应用运行时环境。Quartz不仅仅是线程和线程管理。Quartz依赖一套松耦合的线程池管理部件来管理线程环境。本篇文章中,我们会多次提到线程池管理,但Quartz里面的每个对象是可配置的或者是可定制的。例如你想要插进自己线程池管理设施,我猜你一定能!Quartz框架有一个丰富的特征集。事实上,Quartz有太多特性以致不能在一种情况中全部领会,但没时间在此详细讨论。Quartz经常会用到cron表达式,可以使用国外网站cronmaker辅助生成cron表达式。

Quartz的组成及原理

Quartz是基于Java线程池实现的。

四个主要部件

1、Job 表示一个工作,要执行的具体内容。此接口中只有一个方法,如下:

void execute(JobExecutionContext context) 

2、JobDetail 表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。

3、Trigger 代表一个调度参数的配置,什么时候去调。

4、Scheduler 代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调度了。

最基础的形式

下面演示Quartz编写的步骤。

Gradle依赖库:

// https://mvnrepository.com/artifact/org.quartz-scheduler/quartz
compile group: 'org.quartz-scheduler', name: 'quartz', version: '2.3.1'

1、 定义Job类

public class HelloJob implements Job {
    public HelloJob() {
    }
    public void execute(JobExecutionContext context)
        throws JobExecutionException {
        System.out.println("Hello World! - " + new Date());
    }
}

2、创建调度器

    SchedulerFactory sf = new StdSchedulerFactory();
    Scheduler sched = sf.getScheduler();

3、创建JobDetail或者说实例化Job

     Date runTime =  evenMinuteDate(new Date());
    JobDetail job = newJob(HelloJob.class).withIdentity("job1", "group1").build();

4、创建调度逻辑

 Trigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(runTime).build();

5、组合Job和trigger,调度器启动,调度器关闭。

  sched.scheduleJob(job, trigger);
  sched.start();
  sched.shutdown(true);

Quartz的使用步骤都是这样的,只不过在不同的应用场景下使用的调度逻辑和Job要变化。

控制Job类的并发

@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class BadJob2 implements Job { 。。。}

这两个注解是控制Job类的并发状态的,可以影响 Quartz 的行为。

@DisallowConcurrentExecution 添加到 Job 类后,Quartz 将不会同时执行一个Job 的多个实例。
Quartz定时任务默认都是并发执行的,不会等待上一次任务执行完毕,只要间隔时间到就会执行, 如果定时任执行太长,会长时间占用资源,导致其它任务堵塞。

@PersistJobDataAfterExecution 添加到 Job 类后,表示 Quartz 将会在成功执行 execute() 方法后(没有抛出异常)更新 JobDetail 的 JobDataMap,下一次执行相同的任务(JobDetail)将会得到更新后的值,而不是原始的值。就像@DisallowConcurrentExecution 一样,这个注释基于 JobDetail 而不是 Job 类的实例。

如果你使用了 @PersistJobDataAfterExecution 注释,那么强烈建议你使用 @DisallowConcurrentExecution 注释,这是为了避免出现并发问题,当多个 Job 实例同时执行的时候,到底使用了哪个数据将变得很混乱。

trigger的时间点定义的几种形式:

 Date startTime = DateBuilder.nextGivenSecondDate(null, 15);
 Date runTime =  evenMinuteDate(new Date());
  Date startTime = nextGivenSecondDate(null, 15);

Job类创建的形式分为两类:1、无参,2、有参

// 无参Job
  JobDetail job = newJob(SimpleJob.class).withIdentity("job1", "group1").build();
// 有参Job
 JobDetail job = newJob(BadJob1.class).withIdentity("badJob1", "group1").usingJobData("denominator", "0").build();

Trigger的两种形式

Trigger的创建分为:1、函数式,2、cron表达式。每种都有不循环和循环执行。

函数式

// 单次执行
    JobDetail job = newJob(HelloJob.class).withIdentity("job1", "group1").build();
    Trigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(runTime).build();
    sched.scheduleJob(job, trigger);
// 循环执行
    job = newJob(SimpleJob.class).withIdentity("job3", "group1").build();
    trigger = newTrigger().withIdentity("trigger3", "group1").startAt(startTime)
        .withSchedule(simpleSchedule().withIntervalInSeconds(3).withRepeatCount(10)).build();
        // 循环执行3次
         //  无限循环用 withRepeatCount().repeatForever()).build();
    ft = sched.scheduleJob(job, trigger);

Cron 表达式 Trigger

Cron表达式的语法网上朋友们的文章写得很全面,所以我就不在此赘述了。

    JobDetail job = newJob(SimpleJob.class).withIdentity("job1", "group1").build();
    CronTrigger trigger = newTrigger().withIdentity("trigger1", "group1").withSchedule(cronSchedule("0/20 * * * * ?"))
        .build();
    Date ft = sched.scheduleJob(job, trigger);
// ############################################################
    job = newJob(SimpleJob.class).withIdentity("job2", "group1").build();
    trigger = newTrigger().withIdentity("trigger2", "group1").withSchedule(cronSchedule("15 0/2 * * * ?")).build();
    ft = sched.scheduleJob(job, trigger);

Cron trigger 应该都是不用定义 startAt时间点的。

Scheduler

Scheduler的方法还是不少的碍于篇幅不说了,不过它的方法大部分都是能见名知意的。

ft = sched.scheduleJob(job, trigger);
sched.start();
sched.shutdown(false);
SchedulerMetaData metaData = sched.getMetaData();

在这里插入图片描述

Job的成员方法execute获取参数

    JobKey jobKey = context.getJobDetail().getKey();
    JobDataMap dataMap = context.getJobDetail().getJobDataMap();
    int denominator = dataMap.getInt("denominator");

Job的execute处理异常

在execute里处理异常通常是:

        JobKey jobKey = context.getJobDetail().getKey();
        _log.info("---" + jobKey + " executing at " + new Date());
        // a contrived example of an exception that
        // will be generated by this job due to a 
        // divide by zero error
        try {
            int zero = 0;
            calculation = 4815 / zero;
        } catch (Exception e) {
            _log.info("--- Error in job!");
            JobExecutionException e2 = 
                new JobExecutionException(e);
            // Quartz will automatically unschedule
            // all triggers associated with this job
            // so that it does not run again
            e2.setUnscheduleAllTriggers(true);
            throw e2;
        }
        _log.info("---" + jobKey + " completed at " + new Date());
    }
    JobKey jobKey = context.getJobDetail().getKey();
    JobDataMap dataMap = context.getJobDetail().getJobDataMap();
    int denominator = dataMap.getInt("denominator");
    System.out.println("---" + jobKey + " executing at " + new Date() + " with denominator " + denominator);
    // a contrived example of an exception that
    // will be generated by this job due to a
    // divide by zero error (only on first run)
    try {
      calculation = 4815 / denominator;
    } catch (Exception e) {
      System.out.println("--- Error in job!");
      JobExecutionException e2 = new JobExecutionException(e);

      // fix denominator so the next time this job run
      // it won't fail again
      dataMap.put("denominator", "1");
      // this job will refire immediately
      e2.setRefireImmediately(true);
      throw e2;
    }
    System.out.println("---" + jobKey + " completed at " + new Date());
  }

Quartz与监听器

Quartz有三种监听器:

  1. SchedulerListener
  2. JobListener
  3. TriggerListener

SchedulerListener 是在 Scheduler 级别的事件产生时得到通知,不管是增加还是移除 Scheduler 中的 Job、Trigger,Scheduler的start和shutdown或者是 Scheduler 遭遇到了严重的错误时。那些事件不光是是关于对 Scheduler 管理的,还关注 Job 或 Trigger 的相关事件。SchedulerListener 接口包含了一系列的回调方法,它们会在 Scheduler 的生命周期中有关键事件发生时被调用。

其它两个Listener都是对应具体组件。
具体事件Quartz开发者都定义的比较清晰就不解释了,有几个注意的点在下面的代码里有注释。

可以在具体组件上加监听器。日志记录Quartz的状态。

SchedulerListener接口
public class SchedulerListenerExample implements SchedulerListener {
    @Override
    public void jobScheduled(Trigger trigger) {
    }
    
    @Override
    public void jobUnscheduled(TriggerKey triggerKey) {
    }

    @Override
    public void triggerFinalized(Trigger trigger) {
    }

    @Override
    public void triggerPaused(TriggerKey triggerKey) {
    }

    @Override
    public void triggersPaused(String s) {
    }

    @Override
    public void triggerResumed(TriggerKey triggerKey) {
    }

    @Override
    public void triggersResumed(String s) {
    }

    @Override
    public void jobAdded(JobDetail jobDetail) {
    }

    @Override
    public void jobDeleted(JobKey jobKey) {
    }

    @Override
    public void jobPaused(JobKey jobKey) {
    }

    @Override
    public void jobsPaused(String s) {
    }

    @Override
    public void jobResumed(JobKey jobKey) {
    }

    @Override
    public void jobsResumed(String s) {
    }

    @Override
    public void schedulerError(String s, SchedulerException e) {
    }

    @Override
    public void schedulerInStandbyMode() {
    }

    @Override
    public void schedulerStarted() {
    }

    @Override
    public void schedulerStarting() {
    }

    @Override
    public void schedulerShutdown() {
    }

    @Override
    public void schedulerShuttingdown() {
    }

    @Override
    public void schedulingDataCleared() {
    }
}

TriggerListener接口
public class TriggerListenerExample implements TriggerListener {
    @Override
    public String getName() { // !!! 不得返回null  !!!
        return "TriggerListenerExampleName";
    }

    @Override
    public void triggerFired(Trigger trigger, JobExecutionContext jobExecutionContext) {
    }

    @Override
    public boolean vetoJobExecution(Trigger trigger, JobExecutionContext jobExecutionContext) {
        return false;
    }

    @Override
    public void triggerMisfired(Trigger trigger) {
    }

    @Override
    public void triggerComplete(Trigger trigger, JobExecutionContext jobExecutionContext, Trigger.CompletedExecutionInstruction completedExecutionInstruction) {
    }
}

JobListener接口
public class JobListenerShopJ implements JobListener {
    @Override
    public String getName() { // !!! 不得返回null  !!!
        return "JobListenerShopJ";
    }

    @Override
    public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {
//      Job 启动时的事件
    }

    @Override
    public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
    }

    @Override
    public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
//        Job 结束 和 出现异常时的事件
//        正常 结束 e 为 null
    }
}
注入监听器
        SchedulerFactory sf = new StdSchedulerFactory();
        Scheduler sched = sf.getScheduler();
        
        sched.getListenerManager().addSchedulerListener(new SchedulerListenerExample());
        sched.getListenerManager().addTriggerListener(new  TriggerListenerExample());
        sched.getListenerManager().addJobListener(new JobListenerShopJ());

Quartz配置文件

quartz.properties

默认配置:

# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#

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.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true

org.quartz.jobStore.misfireThreshold: 60000

org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

可以对一些改变一些设置做匹配业务、场景优化等。
基本配置有:线程数量、线程优先级、线程池类等。
将修改过的 quartz.properties 文件放到 ClassPath路径(如:Resource路径 下),应用重启时会自动加载配置文件。在改动配置文件时要把默认配置的所有内容带上,不能有缺省。

Quartz的一个异常“Job 创建失败”

Maven使用

  <exclusions>
    <exclusion>
      <groupId>quartz</groupId>
      <artifactId>quartz</artifactId>
    </exclusion>
  </exclusions>

消除传递依赖可以解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值