什么是Quartz
Quartz是一个完全由Java编写的开源作业调度框架,为在Java应用程序中进行作业调度提供了简单却强大的机制。
Quartz允许开发人员根据时间间隔来调度作业。
它实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联。简单地创建一个org.quarz.Job接口的Java类。
Quartz的特点
作为一个优秀的开源调度框架,Quartz 具有以下特点:
① 强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求;
② 灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式;
③ 分布式和集群能力,Terracotta 收购后在原来功能基础上作了进一步提升。
④ Quartz 很容易与 Spring 集成实现灵活可配置的调度功能。
Quartz专用词汇说明
下面是本文中用到的一些专用词汇,在此声明:
scheduler:任务调度器
trigger:触发器,用于定义任务调度时间规则
job:任务,即被调度的任务
misfire:错过的,指本来应该被执行但实际没有被执行的任务调度
Quartz任务调度基本实现原理:
Quartz任务调度的核心元素为:Scheduler——任务调度器、Trigger——触发器、Job——任务。
其中trigger和job是任务调度的元数据,scheduler是实际执行调度的控制器。
Trigger
- 是用于定义调度时间的元素,即按照什么时间规则去执行任务。
- Quartz中主要提供了四种类型的trigger:SimpleTrigger,CronTirgger,DateIntervalTrigger,和NthIncludedDayTrigger。
- 这四种trigger可以满足企业应用中的绝大部分需求。
Job
- 用于表示被调度的任务。
- 主要有两种类型的job:无状态的(stateless)和有状态的(stateful)。
- 对于同一个trigger来说,有状态的job不能被并行执行,只有上一次触发的任务被执行完之后,才能触发下一次执行。
- Job主要有两种属性:volatility和durability,其中volatility表示任务是否被持久化到数据库存储,而durability表示在没有trigger关联的时候任务是否被保留。两者都是在值为true的时候任务被持久化或保留。
- 一个job可以被多个trigger关联,但是一个trigger只能关联一个job。
Scheduler
- scheduler由scheduler工厂创建:DirectSchedulerFactory或者StdSchedulerFactory。
- StdSchedulerFactory使用较多,因为DirectSchedulerFactory使用起来不够方便,需要作许多详细的手工编码设置。
- Scheduler主要有三种:RemoteMBeanScheduler,RemoteScheduler和StdScheduler。StdScheduler 为最常用。
Quartz 线程视图
在Quartz中,有两类线程,Scheduler调度线程和任务执行线程,其中任务执行线程通常使用一个线程池维护一组线程。
Scheduler调度线程主要有两个:执行常规调度的线程,和执行misfiredtrigger的线程。
常规调度线程:轮询存储的所有trigger,如果有需要触发的trigger,即到达了下一次触发的时间,则从任务执行线程池获取一个空闲线程,执行与该trigger关联的任务。
Misfire线程:是扫描所有的trigger,查看是否有misfiredtrigger,如果有的话根据misfire的策略分别处理(fire now OR wait for the next fire)。
Quartz Job数据存储
Quartz中的trigger和job需要存储下来才能被使用。
Quartz中有两种存储方式:RAMJobStore,JobStoreSupport。
RAMJobStore是将trigger和job存储在内存中,而JobStoreSupport是基于jdbc将trigger和job存储到数据库中。
RAMJobStore的存取速度非常快,但是由于其在系统被停止后所有的数据都会丢失,所以在集群应用中,必须使用JobStoreSupport。
任务调度器调度的时序:
在这里将几个重要的类调用的过程以序列图的形式展现出来,上半部分展现的是启动过程,下半部分展现的是任务调度的过程。
步骤1.用户首先需要生成一个调度器工厂SchedulerFactory,可以用下面的方式实现自己的定制化:
Properties properties=new Properties(); properties.put("org.quartz.threadPool.class","org.quartz.simpl.SimpleThreadPool"); properties.put("org.quartz.threadPool.threadCount","10"); SchedulerFactory sf=new StdSchedulerFactory(properties);
步骤2.然后通过getScheduler()方法从调度器工厂里得到调度器实例,首先查找有没有这样的调度器,没有的话,就生成一个,有的话直接返回。所以得到的一般是单例,即默认的调度器。
步骤3.Scheduler有一个QuartzSchedulerThread(Thread的子类)属性,在scheduler实例化的时候,实例化了一个对象,并用ThreadExecutor启动该线程对象。该线程就是调度线程,主要任务就是不停的从JobStore中获取即将被触发的触发器(默认30s调度一次)。在这个时候调度线程虽然启动,但是处于pause状态。
步骤4.接下来是任务调度的部分:
Scheduler scheduler=sf.getScheduler(); scheduler.addJobListener(new TaskListener()); scheduler.scheduleJob(jobDetail, simpleTrigger); scheduler.start();
client通过scheduleJob()方法将任务和触发器存储在JobStore中,通过start()方法将QuartzSchedulerThread的pause状态设为false,通知调度线程执行任务,此后调度线程不停的从JobStore中去取即将触发的任务。
任务执行的时序:
上半部分展现的是任务执行之前准备工作的时序,下半部分展现的是任务执行的时序。
步骤1.调度线程首先去线程池中获取可用的线程,如果没有的话,就阻塞。
步骤2.从JobStore(从存储介质中获取触发器,存储介质可以是内存也可以是数据库)获取(接下来30s内的)触发器,然后等待该触发器触发。
步骤3.调度线程创建一个JobRunShell(就是一个Runnable),然后从线程池中调用线程执行该任务。
接下来就是任务执行的时序:
步骤4.获取trigger、JobDetail以及生成Job实例,然后执行job的execute接口函数。
持久化的任务的执行时序:
以上就是Quartz的基本工作流程。
参考来源:https://www.cnblogs.com/zhangchengzhangtuo/p/5705672.html