Hudson报表系统二期结束了,这次新增了邮件定制功能,实现此功能的核心在于quartz框架。
Quartz是什么
Quartz是一个用Java编写的任务调度框架,任务调度是什么,举例说明:比如我们需要在每个星期四下午三点时候发周报,我们需要一个系统在两点半的 时候给我们一个发周报的提醒,这个提醒就是一次任务,每周星期四下午两点半的时候自动触发这个任务,这就可以理解为这个系统的一次任务调度。Quartz 提供给我们定时调度已定义好的任务的能力,如果你了解quartz的调度计划cronExpression表达式的配置,你会感叹quartz的灵活与强 大。
Quartz的应用广泛,对于Hudson报表系统,恰好为邮件定制功能提供强有力的支持。
Quartz的简单使用
在Hudson报表系统的一期中也用到了quartz,不过当时是把任务调度信息写死在配置文件中,每次系统启动后,调度的配置都加载在内存中,没有实现 多任务的动态加载。我们先以此来说明quartz的基本用法。Hudson报表系统是用spring框架将quartz整合起来的,故只介绍spring 和quartz的整合使用。
下图为spring配置文件中quartz的配置:
其实quartz的使用主要有三个部分,一个调度器Scheduler,一个要被调度的任务JobDetail,一个触发器Trigger,对上面的配置做一说明,在图中从下到上:
1、 配置一个job,这个job就是我们要调度的任务类,
2、 配置jobDetail,jobDetail需要加载job实现类,并且指定目标方法即完成任务需要执行的方法为execute,在这个方法里面定义我们要做的任务,execute方法需要我们在job类中实现。
3、 配置trigger,用于触发我们定义的任务,在触发器的配置中,我们需要加载需要调度的任务jobDetail,当然还要配置好我们需要触发的时间,触 发的时间配置在cronExperssion表达式中,这个表达式非常灵活与强大,举个例子:我们需要每个工作日早八点到晚八点之间,每半个小时触发一次 任务,我们可以将表达式配置为:0 0/30 8-20 ? * MON-FRI。这个表达式的详细解释可以登录Hudson报表系统(链接:http://10.232.29.21:8080/hudson-report/),点击邮件信息维护里面的“邮件发送时间计划配置帮助”查看。
4、 配置scheduler,只需要加载trigger即可,可以在list标签下配置多个trigger。注意,scheduler的配置中有个lazy- init=false的配置,这表明只要我们的spring一启动,quartz的scheduler也跟着启动,否则其值为true的话,会因为惰性加 载的问题,导致quartz不起作用,此项默认值是false。
至此,我们的配置写好了,只需要实现拥有execute方法的job类就可以了。
这种方法也可以实现多任务调度,具体做法为:我们每新增一个调度,就需要在配置文件中多加这个调度相关的配置,这样显然很麻烦。
Quartz的多任务动态加载
先做简要说明:
多个任务的动态加载是指:在当前的scheduler中已经有可触发的任务的情况下,我们需要新增一条任务进去,并且使得新增的任务也立即加载到 scheduler中,等待触发。要实现这样的功能,上面的方法明显不能达到,而且我们需要使得我们的任务状态持久化,即每次重启quartz后,自动加 载重启前拥有的任务,把任务保存于内存也明显不能达到此目的。怎么办?添加数据库支持,将信息保存于数据表中。
Quartz的官方文档中提供了在各种类型数据库中建立数据表的sql文件(/docs/dbTables目录下),我们采用MySQL数据库可以选择 tables_mysql_innodb.sql这个文件。这里需要注意的是编码,quartz默认采用latin1编码,我们一般用urf-8或者 gbk编码,建立表的时候会提示表中存在过长的字段,怎么办,改编码?但我们需要中文字符,所以我们可以将过长的字段长度改为支持的长度即可,现阶段并没 有发现副作用。
建立起来的数据表如下图:
共十二张表,从上到下依次解释:
qrtz_blob_triggers:
qrtz_calendars:存放日历信息, quartz可配置一个日历来指定一个时间范围。
qrtz_cron_triggers:存放cron类型的触发器
qrtz_fired_triggers:存放已触发的触发器
qrtz_job_details:存放一个jobDetail信息
qrtz_job_listeners:job监听器
qrtz_locks:
qrtz_paused_trigger_graps:存放暂停掉的触发器
qrtz_scheduler_state:调度器状态
qrtz_simple_triggers:简单触发器的信息
qrtz_trigger_listeners:触发器监听器
qrtz_triggers:触发器的基本信息
Quartz的触发时间的配置有三种方式:
cron的方式:采用cronExpression表达式配置时间;
simple的方式,和JavaTimer差不多,可以指定一个开始时间和结束时间外加一个循环时间;
calendars方式,可以和cron配合使用,用cron表达式指定一个触发时间规律,用calendar指定一个范围。
我们采用的是cron方式,需要用到的数据表主要是:qrtz_triggers ,qrtz_cron_triggers,qrtz_fired_triggers,qrtz_job_details。
到这里开始讲到底怎么来实现我们的多任务动态加载。
其实我们要实现我们的多任务动态调度根本不需要了解这些表,我们要做的只是实现一个做任务的job类,然后新建一个jobDetail,设置参数,新建一个trigger,设置参数,一起加入到scheduler里面去就行了。具体过程如下:
1、 建立数据表,配置好数据库连接。
2、 在Spring的配置文件中配置好schedule:
其中dataSource配置的是数据库连接,threadCount配置的是容许同时5个任务触发,
startupDelay配置的是触发启动后的时延,这些配置的具体信息大家可以阅读quartz的官方帮助文档来了解。
3、 建立一个用于提供schedule服务的接口ScheduleService及其实现类ScheduleServiceImpl,
这里提供scheduleJob的方法作为示例,以我们的邮件任务为例,我们直接传入一条邮件记录对象,这个对象拥有id,name,收件人,抄送人,主题,还要有一个cronExpression表达式用于定时发送,看具体实现:
在上面的方法中,我们要新增一条邮件任务的时候,拿到这个邮件信息对象,先新建一个JobDetail对象,设置参数,setName方法可以给 jobDetail对象指定一个名字,我们希望一条邮件信息对象对应一个jobDetail,故把邮件信息对象的id传入,否则假如两次新增任务时 jobDetail的名字一样,那么后者会覆盖前者的数据,使得前者失效。JopDataMap里面可以保存我们需要传入的业务参数,邮件信息的参数就保 存在邮件信息对象里面,所以我们把整个对象传入,最重要的是setJobClass这个方法,设置了要处理我们任务的类,使得任务触发后quartz知道 去哪里执行任务。最后将此任务加入scheduler。
然后新建一个CronTrigger对象,构造对象的时候就传入trigger自己的名字和所在组,jobDetail的名字和所在组,设置好 cronExpression表达式,这样此trigger就会按此表达式的计划做触发。需要说明的是:一个jobDetail可以对应多个 trigger,只要在构造时设置trigger名字不同,而jobDetail的名字相同,就可以为相同的jobDetail建立不同的trigger 触发器。而且对于trigger也可以设置jobDataMap,保存此触发器触发时需要的业务参数。
最后SchedulerJob方法把trigger加入scheduler,等待触发。
上万个都可以的。Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的日程序表。Jobs可以做成标准的Java组件或 EJBs。Quartz的最新版本为Quartz 1.8.0。} import static org.quartz.CronScheduleBuilder.cronScheduleimport static org.quartz.JobBuilder.newJobimport static org.quartz.TriggerBuilder.newTriggerimport java.text.SimpleDateFormatimport java.util.Dateimport org.quartz.CronTriggerimport org.quartz.JobDetailimport org.quartz.Schedulerimport org.quartz.SchedulerFactoryimport org.quartz.impl.StdSchedulerFactorypublic class Test {public void go() throws Exception {// 首先,必需要取得一个Scheduler的引用 SchedulerFactory sf = new StdSchedulerFactory() Scheduler sched = sf.getScheduler() //jobs可以在scheduled的sched.start()方法前被调用//job 1将每隔30分钟执行一次JobDetail job = newJob(myJob.class).withIdentity("job1", "group1").build() CronTrigger trigger = newTrigger().withIdentity("trigger1", "group1").withSchedule(cronSchedule("0 0/30 * * * ?")).build() Date ft = sched.scheduleJob(job, trigger) SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS") System.out.pintln("aa");// 开始执行,start()方法被调用后,计时器就开始工作,计时调度中允许放入N个Job sched.start() try {//主线程等待一分钟Thread.sleep(60L * 1000L) } catch (Exception e) {} //关闭定时调度,定时器不再工作 sched.shutdown(true)}public static void main(String[] args) throws Exception {Test test = new Test() test.go() }}欢迎分享,转载请注明来源:内存溢出
评论列表(0条)