Quartz 教程

1、关于 Quartz

Quartz logo

  • Quartz 是一个彻底由 Java 编写的开源做业调度框架,为在 Java 应用程序中进行做业调度提供了简单却强大的机制。
  • Quartz 能够与 J2EE 与 J2SE 应用程序相结合也能够单独使用。
  • Quartz 容许程序开发人员根据时间的间隔来调度做业。
  • Quartz 实现了做业和触发器的多对多的关系,还能把多个做业与不一样的触发器关联。

2、Quartz 核心概念

核心组件

  • Scheduler:调度容器
  • Job:Job接口类,即被调度的任务
  • JobDetail :Job的描述类,job执行时的依据此对象的信息反射实例化出Job的具体执行对象。
  • Trigger:触发器,存放Job执行的时间策略。用于定义任务调度时间规则。
  • JobStore: 存储做业和调度期间的状态
  • Calendar:指定排除的时间点(如排除法定节假日)

job

Job 是一个接口,只有一个方法 void execute(JobExecutionContext context),开发者实现接口来定义任务。JobExecutionContext 类提供了调度上下文的各类信息。Job 运行时的信息保存在 JobDataMap 实例中。例如:java

public class HelloJob implements BaseJob {
    private static Logger _log = LoggerFactory.getLogger(HelloJob.class);  
    public HelloJob() { }  
    public void execute(JobExecutionContext context) throws JobExecutionException {
        _log.error("Hello Job执行时间: " + new Date());
    }
}

JobDetailImpl 类 / JobDetail 接口

JobDetailImpl类实现了JobDetail接口,用来描述一个 job,定义了job全部属性及其 get/set 方法。下面是 job 内部的主要属性:mysql

属性名 说明
class 必须是job实现类(好比JobImpl),用来绑定一个具体job
name job 名称。若是未指定,会自动分配一个惟一名称。全部job都必须拥有一个惟一name,若是两个 job 的name重复,则只有最前面的 job 能被调度
group job 所属的组名
description job描述
durability 是否持久化。若是job设置为非持久,当没有活跃的trigger与之关联的时候,job 会自动从scheduler中删除。也就是说,非持久job的生命期是由trigger的存在与否决定的
shouldRecover 是否可恢复。若是 job 设置为可恢复,一旦 job 执行时scheduler发生hard shutdown(好比进程崩溃或关机),当scheduler重启后,该job会被从新执行
jobDataMap 除了上面常规属性外,用户能够把任意kv数据存入jobDataMap,实现 job 属性的无限制扩展,执行 job 时可使用这些属性数据。此属性的类型是JobDataMap,实现了Serializable接口,可作跨平台的序列化传输

Trigger

是一个类,描述触发Job执行的时间触发规则。主要有 SimpleTriggerCronTrigger 这两个子类。当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择;而CronTrigger则能够经过Cron表达式定义出各类复杂时间规则的调度方案:如每早晨9:00执行,周1、周3、周五下午5:00执行等;git

如下是 trigger 的属性:github

属性名 属性类型 说明
name 全部trigger通用 trigger名称
group 全部trigger通用 trigger所属的组名
description 全部trigger通用 trigger描述
calendarName 全部trigger通用 日历名称,指定使用哪一个Calendar类,常常用来从trigger的调度计划中排除某些时间段
misfireInstruction 全部trigger通用 错过job(未在指定时间执行的job)的处理策略,默认为MISFIRE_INSTRUCTION_SMART_POLICY。详见这篇blog^Quartz misfire
priority 全部trigger通用 优先级,默认为5。当多个trigger同时触发job时,线程池可能不够用,此时根据优先级来决定谁先触发
jobDataMap 全部trigger通用 同job的jobDataMap。假如job和trigger的jobDataMap有同名key,经过getMergedJobDataMap()获取的jobDataMap,将以trigger的为准
startTime 全部trigger通用 触发开始时间,默认为当前时间。决定什么时间开始触发job
endTime 全部trigger通用 触发结束时间。决定什么时间中止触发job
nextFireTime SimpleTrigger私有 下一次触发job的时间
previousFireTime SimpleTrigger私有 上一次触发job的时间
repeatCount SimpleTrigger私有 需触发的总次数
timesTriggered SimpleTrigger私有 已经触发过的次数
repeatInterval SimpleTrigger私有 触发间隔时间

Calendar

org.quartz.Calendarjava.util.Calendar不一样,它是一些日历特定时间点的集合(能够简单地将org.quartz.Calendar看做java.util.Calendar的集合——java.util.Calendar表明一个日历时间点,无特殊说明后面的Calendar即指org.quartz.Calendar)。一个Trigger能够和多个Calendar关联,以便排除或包含某些时间点。假设,咱们安排每周星期一早上10:00执行任务,可是若是碰到法定的节日,任务则不执行,这时就须要在Trigger触发机制的基础上使用Calendar进行定点排除。sql

Scheduler

调度器,表明一个Quartz的独立运行容器,比如一个『大管家』,这个大管家应该能够接受 Job, 而后按照各类Trigger去运行,TriggerJobDetail能够注册到Scheduler中,二者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须惟一,JobDetail的组和名称也必须惟一(但能够和Trigger的组和名称相同,由于它们是不一样类型的)。Scheduler定义了多个接口方法,容许外部经过组及名称访问和控制容器中Trigger和JobDetail。数据库

image Scheduler 能够将 Trigger 绑定到某一 JobDetail 中,这样当 Trigger 触发时,对应的 Job 就被执行。能够经过 SchedulerFactory建立一个 Scheduler 实例。Scheduler 拥有一个 SchedulerContext,它相似于 ServletContext,保存着 Scheduler 上下文信息,Job 和 Trigger 均可以访问 SchedulerContext 内的信息。SchedulerContext 内部经过一个 Map,以键值对的方式维护这些上下文数据,SchedulerContext 为保存和获取数据提供了多个 put() 和 getXxx() 的方法。能够经过Scheduler# getContext()获取对应的SchedulerContext实例;并发

ThreadPool

Scheduler 使用一个线程池做为任务运行的基础设施,任务经过共享线程池中的线程提升运行效率。框架

进行一个定时任务的简单实例

public class JobTest implements BaseJob {
	private static org.slf4j.Logger log = LoggerFactory.getLogger(JobTest.class);

	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		log.error("JobTest 执行时间: " + new Date());
	}
}
@Test
public void quartzTest() throws SchedulerException{
    // 1. 建立 SchedulerFactory
    SchedulerFactory factory = new StdSchedulerFactory();
    // 2. 从工厂中获取调度器实例
    Scheduler scheduler = factory.getScheduler();

    // 3. 引进做业程序
    JobDetail jobDetail = JobBuilder.newJob(JobTest.class).withDescription("this is a ram job") //job的描述
            .withIdentity("jobTest", "jobTestGrip") //job 的name和group
            .build();

    long time=  System.currentTimeMillis() + 3*1000L; //3秒后启动任务
    Date statTime = new Date(time);

    // 4. 建立Trigger
    //使用SimpleScheduleBuilder或者CronScheduleBuilder
    Trigger trigger = TriggerBuilder.newTrigger()
            .withDescription("this is a cronTrigger")
            .withIdentity("jobTrigger", "jobTriggerGroup")
            //.withSchedule(SimpleScheduleBuilder.simpleSchedule())
            .startAt(statTime)  //默认当前时间启动
            .withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")) //两秒执行一次
            .build();

    // 5. 注册任务和定时器
    scheduler.scheduleJob(jobDetail, trigger);

    // 6. 启动 调度器
    scheduler.start();
    _log.info("启动时间 : " + new Date());
}

3、Quartz 设计分析

quartz.properties文件

Quartz 有一个叫作quartz.properties的配置文件,它容许你修改框架运行时环境。缺省是使用 Quartz.jar 里面的quartz.properties 文件。你应该建立一个 quartz.properties 文件的副本而且把它放入你工程的 classes 目录中以便类装载器找到它。ide

// 调度标识名 集群中每个实例都必须使用相同的名称 (区分特定的调度器实例) 
org.quartz.scheduler.instanceName:DefaultQuartzScheduler 
// ID设置为自动获取 每个必须不一样 (全部调度器实例中是惟一的) 
org.quartz.scheduler.instanceId :AUTO 
// 数据保存方式为持久化 
org.quartz.jobStore.class :org.quartz.impl.jdbcjobstore.JobStoreTX 
// 表的前缀 
org.quartz.jobStore.tablePrefix : QRTZ_ 
// 设置为TRUE不会出现序列化非字符串类到 BLOB 时产生的类版本问题 
// org.quartz.jobStore.useProperties : true 
// 加入集群 true 为集群 false不是集群 
org.quartz.jobStore.isClustered : false 
// 调度实例失效的检查时间间隔 
org.quartz.jobStore.clusterCheckinInterval:20000 
// 允许的最大做业延长时间 
org.quartz.jobStore.misfireThreshold :60000 
// ThreadPool 实现的类名 
org.quartz.threadPool.class:org.quartz.simpl.SimpleThreadPool 
// 线程数量 
org.quartz.threadPool.threadCount : 10 
// 线程优先级 
// threadPriority 属性的最大值是常量 java.lang.Thread.MAX_PRIORITY,等于10。最小值为常量 java.lang.Thread.MIN_PRIORITY,为1
org.quartz.threadPool.threadPriority : 5
// 自建立父线程 
//org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true 
// 数据库别名 
org.quartz.jobStore.dataSource : qzDS 
// 设置数据源 
org.quartz.dataSource.qzDS.driver:com.mysql.jdbc.Driver 
org.quartz.dataSource.qzDS.URL:jdbc:mysql://localhost:3306/quartz 
org.quartz.dataSource.qzDS.user:root 
org.quartz.dataSource.qzDS.password:123456 
org.quartz.dataSource.qzDS.maxConnection:10

Quartz 调度器

Quartz框架的核心是调度器。调度器负责管理Quartz应用运行时环境。启动时,框架初始化一套worker线程,这套线程被调度器用来执行预约的做业。这就是 Quartz 怎样能并发运行多个做业的原理。Quartz 依赖一套松耦合的线程池管理部件来管理线程环境。工具

两种做业存储方式

1. RAMJobStore

- 一般的内存来持久化调度程序信息。这种做业存储类型最容易配置、构造和运行。
- 由于这种方式的调度程序信息是被分配到 JVM 内存中,因此,当应用程序中止运行时,全部调度信息将被丢失。若是你须要在从新启动之间持久化调度信息,则将须要第二种类型的做业存储。

2. JDBC做业存储

- 须要JDBC驱动程序和后台数据库来持久化调度程序信息(支持集群)
表关系和解释

表关系

表名称 说明
qrtz_blob_triggers Trigger做为Blob类型存储(用于Quartz用户用JDBC建立他们本身定制的Trigger类型,JobStore 并不知道如何存储实例的时候)
qrtz_calendars 以Blob类型存储Quartz的Calendar日历信息, quartz可配置一个日从来指定一个时间范围
qrtz_cron_triggers 存储Cron Trigger,包括Cron表达式和时区信息。
qrtz_fired_triggers 存储与已触发的Trigger相关的状态信息,以及相联Job的执行信息
qrtz_job_details 存储每个已配置的Job的详细信息
qrtz_locks 存储程序的非观锁的信息(假如使用了悲观锁)
qrtz_paused_trigger_graps 存储已暂停的Trigger组的信息
qrtz_scheduler_state 存储少许的有关 Scheduler的状态信息,和别的 Scheduler 实例(假如是用于一个集群中)
qrtz_simple_triggers 存储简单的 Trigger,包括重复次数,间隔,以及已触的次数
qrtz_triggers 存储已配置的 Trigger的信息
qrzt_simprop_triggers

利用 SpringBoot + Quartz 搭建的界面化的 Demo

在网上找到一个搭好的 Demo,感谢大神!原文: Spring Boot集成持久化Quartz定时任务管理和界面展现

本工程所用到的技术或工具

Spring Boot
Mybatis
Quartz
PageHelper
VueJS
ElementUI
MySql数据库

先看图:

效果图.png 新建任务.png

源码地址

参考资料

相关文章
相关标签/搜索