天天看点

Quartz任务调度(1)概念例析快速入门Quartz框架需求引入实例解析概念具体用法详解

1. 允许我们灵活而细粒度地设置任务触发时间,比如常见的每隔多长时间,或每天特定时刻、特定日子(如节假日),都能灵活地配置这些成相应时间点来触发我们的任务

2. 提供了调度环境的持久化机制,可以将任务内容保存到数据库中,在完成后删除记录,这样就能避免因系统故障而任务尚未执行且不再执行的问题。

在quartz中,有几个核心类和接口:job、jobdetail、trigger、calendar、scheduler。

下面我们结合实例来分析这些类的角色定位。

现在我们有一个新闻网站,它有一张任务日志表,记录着我们的不同任务,比如每隔三十分钟要根据文章的阅读量和评论量来生成我们的最热文章列表。在每天早晚12点,定时从其他新闻网站扒取一定量新闻,在每周一晚上12点到3点进行论坛封闭维护,而如果遇到节假日则不维护等。在以上实例中:

生成最热文章,扒取新闻,论坛封闭维护都是我们的job,它定义了我们的需要执行的任务,是一个抽象的接口. 如果我们要具体到每隔三十分钟生成最热文章,早晚12点扒取新闻等,在我们的具体任务执行时刻,我们就需要能够描述job及其他相关静态信息的jobdetail,它相当于是我们的job+具体实现细节 而trigger则描述了job执行的时间触发规则,比如每隔三十分钟、早晚12点等 而这里的calendar可以看成是一些日历特定时间点的集合,比如我们这里遇到节假日则不维护,节假日如国庆节、愚人节等等,都是我们的日历特定时间点。 而scheduler就是我们的任务日志表,它是一个容器,记载(容纳)了我们前面的工作、触发时间等内容。

通过实例,我们对quartz的核心类有了较清晰的功能定位,根据quratz的不同版本,这几个核心类有较大改动,具体的操作不太相同,但是思路是相同的;比如1.+版本jar包中,jobdetail是个类,直接通过构造方法与job类关联。simpletrigger和corntrigger是类;在2.+jar包中,jobdetail是个接口,simpletrigger和corntrigger是接口。下面详细地分析它们的具体用法:

job是一个接口,只有一个void execute(jobexecutioncontext jec)方法,jobexecutioncontext提供了我们的任务调度上下文信息,比如,我们可以通过jobexecutioncontext获取job相对应的jobdetail、trigger等信息,我们在配置自己的内容时,需要实现此类,并在execute中重写我们的任务内容。下面是我们的扒取新闻工作实例:

quartz在每次执行任务时,都会创建一个job实例,并为其配置上下文信息,jobdetail有一个成员属性jobdatamap,存放了我们job运行时的具体信息,在后面我们会详细提到。

1. 在1.+版本中,它作为一个类,常用的构造方法有:jobdetail(string name, string group, class jobclass),我们需要指定job的名称,组别和实现了job接口的自定义任务类。实例如<code>jobdetail jobdetail =new jobdetail("job1", "jgroup1", picknewsjob.class);</code>

2. 而在2.+版本中,我们则通过一下方法创建 <code>jobbuilder.newjob(自定义任务类).withidentity(任务名称,组名).build();实例如</code>jobdetail jobdetail = jobbuilder.newjob(picknewsjob.class).withidentity(“job1”,”group1”).build();`

先讲scheduler,方便后讲解trigger时测试。

scheduler作为我们的“任务记录表”,里面(可以)配置大量的trigger和jobdetail,两者在 scheduler中拥有各自的组及名称,组及名称是scheduler查找定位容器中某一对象的依据,trigger的组及名称必须唯一,jobdetail的组和名称也必须唯一(但可以和trigger的组和名称相同,因为它们是不同类型的)。scheduler可以将trigger绑定到某一jobdetail中,这样当trigger触发时,对应的job就被执行。一个job可以对应多个trigger,但一个trigger只能对应一个job。可以通过schedulerfactory创建一个scheduler实例。下面是使用schduler的实例:

在一个scheduler被创建后,它处于”stand-by”模式,在触发任何job前需要使用它的start()方法来启动。同样的,如果我们想根据我们的业务逻辑来停止定时方案执行,可以使用scheduler.standby()方法

trigger描述了job执行的时间触发规则,主要有simpletrigger和crontrigger两个子类。

如果嵌入事件机制只触发一次,或意图使job以固定时间间隔触发,则使用simpletrigger较合适,它有多个构造函数,其中一个最复杂的构造函数为:

<code>simpletrigger(string name, string group, string jobname, string jobgroup, date starttime, date endtime, int repeatcount, long repeatinterval)</code>参数依次为触发器名称、触发器所在组名、工作名、工作所在组名、开始日期、结束日期、重复次数、重复间隔。

1. 如果我们不需同时设置这么多属性,可调用其他只有部分参数的构造方法,其他参数也可以通过set方法动态设置。

2. 这里需要注意的是,如果到了我们设置的endtime,即时重复次数repeatcount还没有达到我们预设置的次数。任务也不会再此执行。

下面是1.+版本的创建实例

下面是2.+版本的创建实例

通过triggerbuilder,我们可以通过方法方便地配置触发器的各种参数。

通过cron表达式定义复杂的时间调度方案,具体内容我们在下一篇详细提到

在实际的开发中,我们可能需要根据节假日来调整我们的任务调度方案。实例如下:

在这里,除了可以使用annualcalendar外,还有croncalendar(表达式),dailycalendar(指定的时间范围内的每一天),holidaycalendar(排除节假日),monthlycalendar(排除月份中的数天),weeklycalendar(排除星期中的一天或多天)

至此,我们的核心类基本讲解完毕,下面附上我们的完整测试代码:

可见,两个不同版本的主要区别在于jobdetail和triiger的配置。

此外,除了使用<code>scheduler.schedulejob(jobdetail, simpletrigger)</code>来建立jobdetail和simpletrigger的关联外,在1.+版本中的配置还可以采用如下所示方式

这里还需要注意的是,如果我们使用<code>scheduler.addcalendar("holidays", holidays, false, false)</code>必须在向scheduler注册trigger之前<code>scheduler.schedulejob(simpletrigger)</code>,否则会抛异常:calendar not found: holidays

而在2.+版本中,我尝试在创建triiger时用forjob(“job1”, “jgroup1”)来绑定job名和组名

//后面是一样的

scheduler.addjob(jobdetail, true);

scheduler.schedulejob(simpletrigger);

在运行时,却会抛出异常: jobs added with no trigger must be durable.

显然是绑定失败了,目前暂未找到解决方法,如果有找到解决方法的朋友,恳请告诉我一下,十分感谢!