Quartz学习笔记

简介

Quartz在结构上由三部分组成:工作(Job),触发器(Trigger),调度器(Scheduler)。

  1. Job指定工作内容 。
  2. Trigger指定工作的触发机制 。
  3. Scheduler = Job + Trigger 即 根据触发机制调用Job。

Job

public class MyJob implement Job{
@Override
	public void execute(JobExecutionContext context) throws JobExecutionException{
	}
}

Job接口只有一个“execute”方法需要实现,该方法指定工作内容。该方法有一个JobExecutionContext参数,这个参数保存着该Job运行时的一些信息,比如:

  • 调度Job的scheduler引用 。
  • 触发Job的trigger引用 。
  • JobDetail对象的引用(稍后详解)。
  • 以及一些其他信息。

execute方法中仅允许抛出一种类型的异常即JobExecutionException,你的Job可以使用异常告诉scheduler,你希望如何来处理发生的异常。

注解

在Job类上可以加一些注解,这些注解会影响到Job的状态和并发性:

  • @DisallowConcurrentExecution:告诉Quartz不要并发地执行同一个Job定义的多个实例。(如果Job类只有一个实例,可以并发地执行该实例)。
  • @PersistJobDataAfterExecution:告诉Quartz在成功执行了Job类的execute方法后,更新JobDetail中JobDataMap的数据。此注解最好与上面的注解一起使用,从而避免并发读写的相关问题。

其他特性

通过JobDetail对象,可以给Job实例配置的其他属性有:

  • Durability:如果一个Job是非持久的,当没有活跃的trigger与之关联的时候,会自动地从scheduler中删除。
  • RequestsRecovery:如果一个Job是可恢复的,并且在其执行的时候,scheduler发生硬关闭,则当scheduler重启时,该Job会被重新执行。

Scheduler

首先创建Scheduler工厂

SchedulerFactory factory = new StdSchedulerFactory();

通过Scheduler工厂获得Scheduler实例

Scheduler scheduler = factory.getScheduler();

Scheduler实例化后,拥有三种状态:启动(start),暂停(stand-by),停止(shutdown)。
Scheduler被停止后,除非重新实例化,否则不能重新启动.只有当Scheduler启动后(即便处于暂停状态也不行),trigger才会被触发(job才会被执行)。

JobDetail

JobDetail对象用来将Job加入到scheduler:

JobDetail jobdetail = JobBuilder.newJob(Myjob.class)
	.withIdentity(“myjob”,”group1”)		//name = myjob, group = group1
	.usingJobDate(“jobSays”,”HelloWorld”)
	.build();

这里的JobBuilder用于构建JobDetail实例。
JobDetail对象中可以包含对Job的各种属性设置,以及用于存储Job实例状态信息的JobDataMap:

  • 上面的withIdentity()指定了该Job的Key(由name和group组成)。
  • 上面的usingJobDate()为该Job添加JobDataMap。

JobDataMap

JobDataMap是Java Map接口的一个实现。

应用场景:首先来了解一下Job实例的生命期,我们传给scheduler一个JobDetail实例,我们在创建JobDetail时,将要执行的job类名传给了JobDetail。所以scheduler知道要执行何种类型的job;每当scheduler执行job时,在调用其execute(…)方法之前会创建一个该类的新实例;执行完毕,对该实例的引用就被丢弃了,实例会被垃圾回收;这种执行策略带来的一个后果是,在Job类中,不应该定义有状态的数据属性,因为在Job的多次执行中,这些属性的值不会保留。

为了给Job增加属性和配置呢,在Job的多次执行中跟踪Job的状态。我们可以使用JobDataMap。下面来演示一下如何在Job中取出由JobDetail配置的JobDataMap:

public class MyJob implement Job{
	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException{
		JobKey key = context.getJobDetail().getkey();	//取Key
		JobDataMap datamap = context.getJobDetail().getJobDataMap();
		/* 
		JobDataMap datamap = context.getMergedJobDataMap();
		JobExecutionContext中的JobDataMap为我们提供了很多的便利。它是JobDetail中的JobDataMap和Trigger中的JobDataMap的并集,但是如果存在相同的数据,则后者会覆盖前者的值。
		*/
		String jobSays = dataMap.getString(“jobsays”);
		System.out.println(jobSays);
	}
}

如果你希望使用JobFactory实现数据的自动注入,则示例代码为:

public class MyJob implement Job{
	String jobSays;
	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException{
		JobKey key = context.getJobDetail().getkey();	//取Key
		JobDataMap datamap = context.getMergedJobDataMap();
		System.out.println(jobSays);
	}
	public void setJobSays(String jobSays){
		This.jobSays = jobSays;
	}
}

Trigger

通过TriggerBuilder构建Trigger实例的时候可以定义一些Trigger的公共属性:

  • jobKey属性:当Trigger触发时被执行的Job的身份。
  • startTime属性:设置Trigger第一次触发的时间。假设现在是一月,设定Trigger每个月五号执行,startTime设定为4月1号,则第一次执行的日期应为4月5号。
  • endTime属性:表示trigger失效的时间点。如果设定Trigger每个月五号执行,endTime设定为5月1号,则最后一次执行的日期应为4月5日。

还有一些其他的属性:

优先级priority

如果你的Trigger很多但是Quartz线程池的工作线程太少,Quartz可能没有足够的资源同时触发所有Trigger。可以通过设置priority来指定Trigger的优先级。
注意:只有同时触发的Trigger之间存在优先级差异,10:01总比10:02的Trigger先执行。

错过触发misfire

在scheduler关闭或没有足够的工作线程来执行Job的情况下,持久性的Trigger就会错过其触发时间 即 错过触发(misfire)。当scheduler启动时会查询所有misfire的持久性Trigger,并根据它们各自的misfire属性更新Trigger信息。

日历示例calendar

Quartz的calendar用于从Trigger的调度计划中排除时间段,比如创建一个Trigger,每个工作日的上午9.30执行,然后增加一个celendar,排除掉所有的商业节日。

任何实现了Calendar接口的可序列化对象都可以作为Calendar对象,Quartz在这里提供了org.quartz.impl.HolidayCalendar类实现排除某些天。实例化HolidayCalendar后,需要调用addExcludedDate(Date date)方法指定需要排除的日期,示例:

HolidayCalendar cal - new HolidayCalendar();
Cal.addExcludedDate(someDate);
scheduler.addCalendar(“myHolidays”,cal,false);	//把calendar对象注册到scheduler
Trigger t = newTrigger()
	.withIdentity("myTrigger")
	.forJob("myJob")
	.withSchedule(dailyAtHourAndMinute(9, 30)) // execute job daily at 9:30
	.modifiedByCalendar("myHolidays") // but not on holidays
	.build();

Simple Trigger

SimpleTrigger可以满足的需求是:

  • 在具体时间执行一次。
  • 在具体时间执行,以指定间隔重复执行若干次。

SimpleTrigger的属性包括:开始时间,结束时间,重复次数以及重复的间隔。
重复的次数可以是0、正整数以及常量。
重复的间隔可以是0或者long型的正数,表示毫秒。如果重复间隔是0,将会以重复的次数并发执行(或者以scheduler可以处理的近似并发数执行)。
示例:

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();
//	指定时间触发,每隔10秒执行一次,重复10次

Cron Trigger

CronTrigger通常比SimpleTrigger更实用,它基于日历的概念去制定计划。
通过cron表达式指定调度计划,cron表达式不做阐述,网上有在线cron表达式生成器。

TriggerListeners和JobListeners

Listeners是您创建的对象,用于根据调度程序中发生的事件执行操作。顾名思义前者接收到与触发器(trigger)相关的事件,后者接收到与jobs相关的事件。
与触发相关的事件包括:触发器触发,触发失灵,触发完成。
org.quartz.TriggerListeners接口:

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);	//触发完成
}

与Job相关的事件包括:Job即将执行的通知 以及 Job完成执行时的通知。
org.quartz.JobListeners接口:

public interface JobListener {
public String getName();
	public void jobToBeExecuted(JobExecutionContext context);	//即将执行
	public void jobExecutionVetoed(JobExecutionContext context);
	public void jobWasExecuted(JobExecutionContext context,JobExecutionException jobException);	//完成执行
}

使用自己的Listeners

要创建自己的listener,只需要实现上述两个接口。为了方便我们可以扩展JobListenerSupport类和TriggerListenerSupport类,只需要覆盖我们感兴趣的事件。
Listener与调度程序的ListenerManager一起注册,并可以配置listener希望接收事件的job/触发器的Matcher。Listener不与Job和触发器一起存储在JobStore中,因为听众通常是与应用程序的集成点。因此,每次运行应用程序时,都需要重新注册该调度程序。

scheduler.getListenerManager().addJobListener(myJobListener,
KeyMatcher.jobKeyEquals(new JobKey(“myJobName”,“myJobGroup”)));

还有一些其他的匹配方式,在这里先不做阐述了,详情可以查看官方文档。

SchedulerListeners

非常类似于TriggerListeners和JobListeners,它还可以在Scheduler本身中接收到事件的通知。
与调度程序相关的事件包括:添加job/触发器,删除job/触发器,调度程序中的严重错误和关闭调度程序等。
org.quartz.SchedulerListener接口:

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);

Job Stores

JobStore负责跟踪我们提供给调度程序的所有“工作数据”:jobs,triggers,日历等。
在SchedulerFactory的属性文件(对象)中指定您的调度程序应使用哪个JobStore(以及它的配置设置)。
切勿在代码中直接使用JobStore实例。

RAMJobStore

RAMJobStore是使用最简单的JobStore,也是性能最高的,它将所有数据保存在内存中。
优点:快! 配置简单!
缺点:当应用程序结束(或崩溃)时,所有调度信息都将丢失。

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

JDBCJobStore

JDBCJobStore通过JDBC将其所有的数据保存到数据库中。因此配置起来会比较麻烦,但是性能下降的不是很糟糕。具体的配置操作可以查看官方文档。

TerracottaJobStore

TerracottaJobStore提供了一种缩放和鲁棒性的手段,而不使用数据库。性能比JDBCJobStore好,但比RAMJobStore慢。

org.quartz.jobStore.class = org.terracotta.quartz.TerracottaJobStore
org.quartz.jobStore.tcConfigUrl = localhost:9510	//添加一个额外的行配置来指定Terracotta服务器的位置

配置,资源使用和SchedulerFactory

Quartz需要配置的组件主要包括:

  • 线程池
  • JobStore
  • DataSources(如有必要)
  • 计划程序本身

ThreadPool接口在org.quartz.spi包中定义,可以通过实现该接口自定义自己的线程池。Quartz自带一个非常令人满意的线程池,几乎所有用户都在使用该线程池。

StdSchedulerFactory

StdSchedulerFactory是org.quartz.SchedulerFactory接口的一个实现。它使用一组属性来创建和初始化Quartz Scheduler。属性通常存储在文件中并从文件中加载,也可以由程序创建并直接传递给工厂。详细属性配置请查看官方文档。

DirectSchedulerFactory

DirectSchedulerFactory是另一个SchedulerFactory的实现。对于希望以更加程序化的方式创建Scheduler实例的用户是有用的。通常不鼓励使用它,原因如下:
(1)要求用户了解他们正在做什么。
(2)它不允许声明性配置。换句话说,你最终会硬编辑所有调度程序的设置。

记录

Quartz使用SLF4J框架来满足所有的日志记录需求。

Quartz高级(企业)功能

Clustering

Clustering目前与JDBCJobStore和TerracottaJobStore一起使用。功能包括负载均衡和job故障转移。详细操作查看官方文档。

JTA交易

Job在JTA事务中执行。详细操作查看官方文档。

Quartz其他功能

插件

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

与Quartz一起提供各种实用功能的插件可以在org.quartz.plugins包中找到。诸如在调度程序启动时自动调用Job,记录Job和触发事件的历史记录,并确保当JVM退出时,调度程序将彻底关闭。

JobFactory

当trigger触发时,通过Scheduler上配置的JobFactory去实例化与之关联的Jobs,默认的JobFactory只是在Job类上调用newInstance()。也可以创建自己的JobFactory实现,以完成诸如让应用程序的IOC或DI容器生成/初始化Job示例之类的操作。

请参阅org.quartz.spi.JobFactory接口以及相关的Scheduler.setJobFactory(fact)方法。

‘Factory-Shipped’ Jobs

Quartz还提供了许多实用的Jobs,我们可以在应用程序中用于执行诸如发送电子邮件和调用EJB等工作,可以在org.quartz.jobs包中找到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值