Quartz实现与组件监听

it2022-05-05  178

Quartz实现与组件监听

Quartz的组成及原理最基础的形式控制Job类的并发Job类创建的形式分为两类:1、无参,2、有参Trigger的两种形式Cron 表达式 TriggerSchedulerJob的成员方法execute获取参数Job的execute处理异常Quartz与监听器SchedulerListener接口TriggerListener接口JobListener接口注入监听器 Quartz配置文件Quartz的一个异常“Job 创建失败”

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有三种监听器:

SchedulerListenerJobListenerTriggerListener

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>

消除传递依赖可以解决。


最新回复(0)