任务调度框架 quartz 和 xxl-job - 上

任务调度框架 quartz 和 xxl-job - 上,第1张

quartz 和 xxl-job 都是任务调度框架,任务调度相关功能都可以借助这两个框架实现。

任务调度指在将来某个特定的时间、固定的时间周期或规律变化的时间周期到达时自动调度并执行指定的任务。

这两个框架都通过自己的方式实现了这个功能,区别在于对任务的创建、修改、删除、触发以及监控的 *** 作成本上,quartz 对于这些 *** 作直接提供了 api,这意味着开发人员拥有最大的 *** 作权,也带来了更高的灵活性,然而对于不需要很高灵活性的系统,或调度任务的 *** 控由非开发人员负责的系统,需要额外对 api 调用做一层封装,隔离 api *** 作;而 xxl-job 提供这些控制的方式是提供了一个单独的可视化调度中心,这意味着任务的状态控制可以和系统分离,通过更易 *** 作的网页界面的形式,降低了对 *** 控者的门槛。

quartz 有四个重要的组件,使用前需要先了解他们。

参考: Quartz 入门详解

1. Scheduler

代表一个 Quartz 的独立运行容器,Scheduler 将 Trigger 绑定到特定 JobDetail, 这样当 Trigger 触发时, 对应的 Job 就会被调度。

2. Trigger

描述 Job 执行的时间触发规则。主要有 SimpleTrigger 和 CronTrigger 两个子类,通过一个 TriggerKey 唯一标识。

3. Job

定义一个任务,规定了任务是执行时的行为。JobExecutionContext 提供了调度器的上下文信息,Job 的数据可从 JobDataMap 中获取。

3. JobDetail

Quartz 在每次执行 Job 时,都重新创建一个 Job 实例,所以它不直接接受一个 Job 的实例,相反它接收一个 Job 实现类。描述 Job 的实现类及其它相关的静态信息,如 Job 名字、描述等。

添加一个任务到 quartz 容器中

ScheduleJob

暂停、恢复、删除一个任务只需调用 Scheduler 的对应方法即可。

CronTrigger 相比 SimpleTrigger 提供了更灵活的调度规则,cron 表达式的知识可参考 cron 表达式详解 。

在线 cron 表达式生成器

Job 实现类

tip:Job 实现类需要用 public 修饰,即不能是内部类。

InitializingBean#afterPropertiesSet 中启动调度器

调度后的控制台日志

demo 为 spring boot 项目,已上传 GitHub ,你可以在 这里 找到。

jdk 1.8

spring boot 集成了 quartz

使用 了 lombok 框架的以下注解:

xxl-job 的分析见下篇 博文

在java中使用quartz

/** *//**

* 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名

* @param jobName 任务名

* @param job 任务

* @param time时间设置,参考quartz说明文档

* @throws SchedulerException

* @throws ParseException

*/

public static void addJob(String jobName,Job job,String time)

throws SchedulerException, ParseException{

Scheduler sched = sf.getScheduler()

JobDetail jobDetail = new JobDetail(jobName, JOB_GROUP_NAME, job.getClass())//任务名,任务组,任务执行类

//触发器

CronTrigger trigger =

new CronTrigger(jobName, TRIGGER_GROUP_NAME)//触发器名,触发器组

trigger.setCronExpression(time)//触发器时间设定

sched.scheduleJob(jobDetail,trigger)

//启动

if(!sched.isShutdown())

sched.start()

}

/** *//**

* 添加一个定时任务

* @param jobName 任务名

* @param jobGroupName 任务组名

* @param triggerName 触发器名

* @param triggerGroupName 触发器组名

* @param job 任务

* @param time时间设置,参考quartz说明文档

* @throws SchedulerException

* @throws ParseException

*/

public static void addJob(String jobName,String jobGroupName,

String triggerName,String triggerGroupName,

Job job,String time)

throws SchedulerException, ParseException{

Scheduler sched = sf.getScheduler()

JobDetail jobDetail = new JobDetail(jobName, jobGroupName, job.getClass())//任务名,任务组,任务执行类

//触发器

CronTrigger trigger =

new CronTrigger(triggerName, triggerGroupName)//触发器名,触发器组

trigger.setCronExpression(time)//触发器时间设定

sched.scheduleJob(jobDetail,trigger)

if(!sched.isShutdown())

sched.start()

}

/** *//**

* 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名)

* @param jobName

* @param time

* @throws SchedulerException

* @throws ParseException

*/

public static void modifyJobTime(String jobName,String time)

throws SchedulerException, ParseException{

Scheduler sched = sf.getScheduler()

Trigger trigger = sched.getTrigger(jobName,TRIGGER_GROUP_NAME)

if(trigger != null){

CronTrigger ct = (CronTrigger)trigger

ct.setCronExpression(time)

sched.resumeTrigger(jobName,TRIGGER_GROUP_NAME)

}

}

/** *//**

* 修改一个任务的触发时间

* @param triggerName

* @param triggerGroupName

* @param time

* @throws SchedulerException

* @throws ParseException

*/

public static void modifyJobTime(String triggerName,String triggerGroupName,

String time)

throws SchedulerException, ParseException{

Scheduler sched = sf.getScheduler()

Trigger trigger = sched.getTrigger(triggerName,triggerGroupName)

if(trigger != null){

CronTrigger ct = (CronTrigger)trigger

//修改时间

ct.setCronExpression(time)

//重启触发器

sched.resumeTrigger(triggerName,triggerGroupName)

}

}

/** *//**

* 移除一个任务(使用默认的任务组名,触发器名,触发器组名)

* @param jobName

* @throws SchedulerException

*/

public static void removeJob(String jobName)

throws SchedulerException{

Scheduler sched = sf.getScheduler()

sched.pauseTrigger(jobName,TRIGGER_GROUP_NAME)//停止触发器

sched.unscheduleJob(jobName,TRIGGER_GROUP_NAME)//移除触发器

sched.deleteJob(jobName,JOB_GROUP_NAME)//删除任务

}

/** *//**

* 移除一个任务

* @param jobName

* @param jobGroupName

* @param triggerName

* @param triggerGroupName

* @throws SchedulerException

*/

public static void removeJob(String jobName,String jobGroupName,

String triggerName,String triggerGroupName)

throws SchedulerException{

Scheduler sched = sf.getScheduler()

sched.pauseTrigger(triggerName,triggerGroupName)//停止触发器

sched.unscheduleJob(triggerName,triggerGroupName)//移除触发器

sched.deleteJob(jobName,jobGroupName)//删除任务

}

用quartz实现多任务动态加载

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,等待触发。


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/bake/11943814.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-19
下一篇 2023-05-19

发表评论

登录后才能评论

评论列表(0条)

保存