一、文章的由来
近来,由于公司运营的系统比较多,而且都是用集群做的负载均衡,从而产生了一些问题需要解决。我们知道,集群环境下,我们写的程序会被部署到集群的每个点上去运行。这种情况下,我们的后台定时任务将会被每个点都执行一遍。如果是全局性质的任务,则会产生逻辑上的错误。一般情况下,全局任务,同一时间只能有一个实例在执行。局部任务可以每个点都(并行调度)运行。
我所要解决的,就是全局任务的调度问题。并且,研发的任务调度系统也要支持集群技术,在任务量扩大的情况下,可以有较强的伸缩性和可靠性。
根据以往经验,发现Quartz本身也支持简单的集群部署能力,于是,对Quartz本身,进行了一次学习过程,将学习成果记录下来,大家有机会共同讨论讨论。
第一章,主要讲述Quartz 的五大组件:JobDetail、Trigger、Scheduler、Listener和Plugin。前三个是任务调度方面的开发接口、后两个可以用于功能增强。
这篇文章主要针对于初学者或没接触过Quartz 的同学。
二、 Quartz 五大组件的介绍
2.1 JobDetail (任务详情)
按照字面意思JobDetail是记录任务的相关信息的一个类(他不是接口),它内部包含了任务的描述信息、任务数据、上一次的执行时间、程序层面的入口类等信息。
任务数据,以Map<String,String>的形式在程序中展现(程序中称为JobDataMap);程序层面的入口类是一个继承了Job接口的实现类,必须含有无参构造器。
Quartz 通过反射加载Job接口的实现类,并且调用无参构造器进行实例化。我们程序员创建的任务,只需要实现Job接口即可,就如同创建线程就要实现Runnable接口一样。
Quartz 的Job,以前版本还有个Statefull 的概念,新版本中取消了,就是说 Job 可不可以叠加运行。比如,执行周期是1秒而执行时间是3秒的任务,可能会被调度器叠加执行。 新版本中,在实现类上加注解@DisallowConcurrentExecution可避免叠加调用。(添加@PersistJobDataAfterExecution 注解可以在任务执行后保存JobDataMap的数据)
2.2 Trigger(触发器)
Quartz 把一个任务拆分成Trigger和Job两个部分,Trigger 是任务执行的时间点(时间点的集合),Job 是任务执行的代码(而Job的实现类保存在JobDetail中)。
Trigger 本身是一个接口类,它有 SimpleTrigger、CronTrigger 和 CalendarTrigger 等几种实现形式。
A、SimpleTrigger 它可以设定一个周期(比如一秒钟)循环往复的进行任务的触发操作,当然,可以设置触发次数(达到后停止触发),开始触发的时间和终止触发的时间。
B、CrongTrigger 它通过设定Cron表达式来确定触发的时间,这是我们最常用的触发器,它的表达式与Linux的Crontab表达式语法基本是一样的。
C、CalandarTrigger 它是一系列时间点的集合,我们并不常用这种触发器,但是,它是最全能的,它可以排除一些法定节假日不进行触发。
最后,我们要注意的是,一个任务可以由多个触发器进行触发,但是,一个触发器不能触发多个任务,只能触发一个任务。
2.3 Scheduler(调度器)
按照字面意思,它是任务和触发器的管理者,是暴露给程序员的开发接口。我们可以通过调度器提交任务和触发器的关系,到了时间点,对应的任务将被执行。当然,我们也可以通过调度器暂停或删除任务(包括触发器)。
调度器不是单例的,在一个程序里,我们可以创建多个调度器(一般不创建多个),来分类管理不同种类的任务。
由于调度器的功能比较富集,所以,它的背后包含有一个ThreadPool和一个JobStore (其他从略)。
A、ThreadPool 是执行任务的线程池,控制线程资源的伸缩性。
B、JobStore 是存放任务及其相关信息的数据结构。通过选择不同的实现类,我们可以选择存放在内存(RAMJobStore)或者 数据库(JobStoreTX或JobStoreCMT)中。
2.4 Listener(监听器)
Quartz 的监听器用于监听触发器和任务的执行状态,共有JobListener和TriggerListener两种,并且,都是全局的。
如果你的程序,需要上述的监听功能,可以灵活编写代码,增强Quartz的功能。
2.5 Plugin(插件)
插件是Quartz 提供给开发人员对其功能扩展的一个编程接口,它是一个接口类。我们如果要对Quartz 进行功能增强,可以考虑写一个插件的实现类。Quartz 启动的时候会同时加载配置好的插件,并初始化插件。
通常情况下,我们会在插件的初始化过程中,添加一些监听器,通过监听器来增强Quartz 的功能,而不是通过强制类型转换到具体的实现类来访问内部数据。
三、 总结
上面就是根据我所理解的 Quartz 的五大组件, 开发人员只要了解了这五大组件的前三种,基本上就可以着手进行Quartz 的开发和使用了,后两种属于Quartz 的功能增强接口。
后面打算针对一些具体的问题展开进行论述,如果有这方面的开发经验的同学,可以共同讨论一下。