1. 简介
Quartz是一个开源的作业调度框架,它完全由Java写成,并设计用于J2SE和J2EE应用中。它提供了巨大的灵 活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。它有很多特征,如:数据库支持,集群,插件,EJB作业预构 建,JavaMail及其它,支持cron-like表达式等等。
2. Quartz架构
3. 核心类
Job:
接口,表示具体要执行的业务逻辑。在实际使用中需要自定义类实现execute方法。
默认情况下,每次Job被执行时都会创建新的Job实例,而执行后的Job实例会被垃圾回收器回收。所以Job是无状态的。
@PersistJobDataAfterExecution 注解表示每一次Job执行成功后会更新JobDataMap的值(存在JobExecutionContext中),后面的Job再执行时会调用新的JobDataMap以此实现状态信息记录。如果不加该注解每次JobDataMap都是默认值。使用该注解建议同时使用@DisallowConcurrentExecution注解使得同一JobDetail不同同时创建多个Job实例,避免并发产生冲突。
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class SaleJob implements Job {
String sellMan;
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println(String.format("%s在%s售出了一件商品",sellMan,Utils.getCurrentFormatTime()));
JobDataMap jobDataMap = context.getMergedJobDataMap();
int count = jobDataMap.getInt("count") + 1;
System.out.println(String.format("目前一共已经售出了%d件商品",count));
}
public void setSellMan(String sellMan) {
this.sellMan = sellMan;
}
}
JobDetail
封装了Job的信息,可以由JobBuilder创建。主要参数包括
- key : JobDetail的唯一标识,由“组名.任务名”构成。
- jobClass : 保存了Job的类字节码,用于使用反射的方式创建Job实例。
- JobDataMap:用于保存可供Job调用的信息,会被封装到JobExecutionContext 中。
- durable:没有活动的trigger与JobDetail关联时JobDetail会被Shceduler回收,将durable参数设置为真则即使没有活动的trigger与该JobDetail关联,Scheduler也不会删除该JobDetail。
- requestRecovery: 表示Scheduler宕机重启后会不会恢复该JobDetail
JobDetail saleJob = JobBuilder.newJob().ofType(SaleJob.class).withIdentity("saleJOb","testGroup") .storeDurably().usingJobData("count",0).build();
注意在一个Scheduler中JobDetail的key是唯一的,不得重复。
Trigger:
用来定义任务的触发时间,主要分为SimpleTrigger和CronTrigger。不过在谈这两者之前先谈谈Trigger的公共属性。
- jobKey: 表示当trigger触发时执行的Job的唯一标识
- startTime: 表示触发器开始工作的时间
- endTime: 表示触发器结束工作的时间
- Priority优先级:默认是5,表示当线程池中的资源不够用时那个Trigger会被优先触发。
- misfireInstructions: 表示错过触发时的策略,默认为MISFIRE_INSTRUCTION_SMART_POLICY,除此之外还有:MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY,MISFIRE_INSTRUCTION_FIRE_NOW,MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT,MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT,MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT,MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT。
- Calendar: org.quartz.Calendar对象(不是util.Calendar),表示跳过触发的日期。
- JobDataMap: 和JobDetail一致,会被存储在JobExcutionContex中。
SimpleTrigger常用于描述只执行一次或从某个时间点开始以固定间隔执行若干次。其可以通过SimpleScheduleBuilder来描述。
Trigger triggerZhangSan = TriggerBuilder.newTrigger().withIdentity("jonTrigger1","testGroup")
.forJob(saleJob).usingJobData("sellMan","张三").startNow().withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)).build();
CronTrigger用于描述更加复杂的触发条件,在构建时需要传入Cron表达式.
Trigger triggerLiSi = TriggerBuilder.newTrigger().withIdentity("jonTrigger2","testGroup")
.forJob(saleJob).usingJobData("sellMan","李四").startNow().withSchedule(CronScheduleBuilder.cronSchedule(JobCron)).build();
Scheduler:
Scheduler调度着任务的执行以及任务执行所需要的资源
Listener (JobListener/ TriggerListener/ SchedulerListener)
Listener是通过AOP实现的,主要作用是接收和处理调度器回调的事件。正对不同的对象有不同的Listener与之相匹配。在实现Listener之后需要将其加入Scheduler.LisenerManager中进行管理。
public class SaleJobListener implements JobListener {
public String getName() {
return "saleJobListener";
}
public void jobToBeExecuted(JobExecutionContext context) {
System.out.println(context.getMergedJobDataMap().getString("sellMan") + "正在招待客户。");
}
public void jobExecutionVetoed(JobExecutionContext context) {
}
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
System.out.println("恭喜" + context.getMergedJobDataMap().getString("sellMan") + "完成销售任务。");
}
}
JobStore
JobStore负责记录你提供到调度器的所有“工作数据”:所有的Job、所有的Trigger、所有的Calendar(org.quartz.Calendar)等等。不建议在代码中使用JobStore实例而是在配置文件中进行配置。目前官方定义的JobStore有三种
- RAMJobStore: 故名思意,使用RAM作为存储介质。速度最快,配置简单,但程序崩溃后信息会丢失
- JDBCJobStore: 通过JDBC将数据保存在数据库中。实现相对复杂,速度较慢,但支持集群模式。在使用之前需要在对应的数据库中进行建表。
- TerracottaJobStore: 性能介于RAMJobStore和JDBCJObStore之间。
4. Quartz分布式调度
Quartz是支持集群的,但只有当JobStore配置为JDBCStore时才支持集群操作。所有的节点公用一个数据库服务,通过数据库行锁实现分布式锁。
下面具体讲讲JDBCStore所依赖的表。
JDBCStore使用的表都是以QRTZ_开头,
- QRTZ_BLOB_TRIGGERS :自定义的triggers使用blog类型进行存储在这张表中
- QRTZ_CALENDARS:
- QRTZ_CRON_TRIGGERS:
- QRTZ_CRON_FIRED_TRIGGERS: 存储已经触发的trigger相关信息
- QRTZ_JOB_DETAILS:
- QRTZ_LOCKS: 存储锁,节点在执行调度时需要先获取需要锁锁名所在行的写锁,由于写锁的互斥性实现了分布式锁。
- QRTZ_PAUSED_TRIGGER_GRPS: 存放暂停掉的触发器
- QRTZ_SCHEDULER_STATE: 存储所有节点的scheduler,会定期检查scheduler是否失效
- QRTZ_SIMPLE_TRIGGES
- QRTZ_SIMPROP_TRIGGERS : 存储CalendarIntervalTrigger和DailyTimeIntervalTrigger两种类型的触发器
- QRTZ_TRIGGERS: 存储定义的trigger