Qtartz

    1. 什么是Quartz

Quartz是一个开源的任务调度框架作用是基于定时、定期的策略来执行任务。

它是OpenSymphony开源组织在Job scheduling领域又一个开源项目。

“任务进度管理器”就是一个在预先被纳入日程时间到达时,负责执行(或者通知)其他软件组件的系统。 简单来说就是实现“计划(或定时)任务”的系统,例如:订单下单后未付款,15分钟后自动撤消订单,并自动解锁锁定的商品。

官网:Quartz Enterprise Job Scheduler

参考:GitHub - dufyun/quartz-core-learning: 关于Quartz技术的学习积累的例子,开始Quartz学习之旅,精进Quartz

    1. Quartz可以用来做什么?

Quartz是一个任务调度框架。比如你遇到这样的问题:

  1. 想每月11号自动提示信用卡还款金额;每月28号,信用卡自动还款。
  2. 订单下单后未付款,15分钟后自动撤消订单,并自动解锁锁定的商品。
  3. 想每年4月1日自己给当年暗恋女神发一封匿名贺卡。
  4. 想每隔1小时,备份一下自己的爱情动作片、学习笔记到云盘。
  5. 医院系统11点才能开放挂号。
  6. 企业中如每天凌晨2点触发数据同步、发送Email等操作。
  7. ……

这些问题总结起来就是:在某一个有规律的时间点干某件事。并且时间的触发的条件可以非常复杂(比如每月最后一个工作日的17:50),复杂到需要一个专门的框架来干这个事。Quartz就是来干这样的事,你给它一个触发条件的定义,它负责到了时间点,触发相应的Job起来干活。

    1. 同类型框架有哪些?

TimeTask:在Quartz前还是显得过于简单、不完善,不能直接满足开发者的较为复杂的应用场景。

Elastic-Job:当当网推出的分布式定时任务框架。

TBSchedule:Alibaba开发的分布式定时任务框架。

  1. Quartz的3个核心要素
    1. 调度器(Scheduler)

一个Quartz的独立运行容器

Scheduler是Quartz的核心,它会将JobDetail及Trigger整合起来,负责基于Trigger设定的触发条件来执行JobDetail中的Job。

Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

scheduler.start(); //启动

scheduler.shutdown(); //关闭

注1:Quartz的Scheduler可直接实现自动装配。

注2:调度器操作:启动、暂停、继续、停止、关闭操作。

    1. 任务(Job)
      1. Job

Job是一个接口,有一个方法 void execute()。

/**
 * 任务类:具体要执行的任务
 * com.example.job.MyJob
 */
public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        //编写我们自己的业务逻辑(任务)

        System.out.println("烤10串鱿鱼须");
    }
}

      1. JobDetail

JobDetail用来装载Job的,可以看成是Job的一个清单。

Quartz每次执行job时,都重新创建一个Job实例,会接收一个Job实现类,以便运行的时候通过newInstance()的反射调用机制去实例化Job.JobDetail是用来描述Job实现类以及相关静态信息

JobDetail为Job实例提供了许多设置属性,以及JobDataMap成员变量属性,它用来存储特定Job实例的状态信息,调度器需要借助JobDetail对象来添加Job实例。

JobDetail jobDetail = newJob(HelloJob.class)

.withIdentity("helloJob", "group1")

.build();

JobDetail重要属性:name、group、jobClass、jobDataMap

// 通过JobBuilder构建JobDetail实例

JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)   // 接受一个Job实例

.withDescription("Job实例的描述")

.withIdentity("helloJob", "group1")     // Job名/组名

.usingJobData("name", "admin")     // Job实例的属性name=admin

.build();    

其它操作:

jobDetail.getKey().getName(); //获得JobDetail中Job的名称

jobDetail.getKey().getGroup(); //获得JobDetail中Job的组名

jobDetail.getJobClass().getName();

    1. 触发器(Trigger)

Trigger为执行任务Job的触发条件。

Quartz提供的5种触发器类型:

  1. SimpleTrigger 简单触发器(或频率触发器)
  2. CronTirgger 表达式(定时)触发器(或日历触发器、七子触发器)
  3. DateIntervalTrigger
  4. NthIncludedDayTrigger
  5. Calendar类(org.quartz.Calendar)
      1. SimpleTrigger简单触发器

作用:执行N次,重复N次。

指定从某一个时间开始,以一定的时间间隔(单位是毫秒)执行的任务。

它适合的任务类似于:9:00 开始,每隔1小时,执行一次。

属性:

  1. repeatInterval 重复间隔
  2. repeatCount 重复次数。实际执行次数是 repeatCount+1。因为在startTime的时候一定会执行一次。** 下面有关repeatCount 属性的都是同理。 **

SimpleTrigger 内部实现机制是通过计算间隔时间来计算下次的执行时间,这就导致其不适合调度定时的任务。例如我们想每天的 1:00AM 执行任务,如果使用 SimpleTrigger 的话间隔时间就是一天。注意这里就会有一个问题,即当有 misfired 的任务并且恢复执行时,该执行时间是随机的(取决于何时执行 misfired 的任务,例如某天的 3:00PM)。这会导致之后每天的执行时间都会变成 3:00PM,而不是我们原来期望的 1:00AM。

      1. CronTrigger表达式触发器(常用)

作用:几时几分几秒,哪天/哪月/哪年,执行,适合于更复杂的任务

类似于SimpleTrigger,指定从某一个时间开始,以一定的时间间隔执行的任务。 但是不同的是SimpleTrigger指定的时间间隔为毫秒,没办法指定每隔一个月执行一次(每月的时间间隔不是固定值),而CalendarIntervalTrigger支持的间隔单位有秒,分钟,小时,天,月,年,星期

相较于SimpleTrigger有两个优势:

  1. 更方便,比如每隔1小时执行,你不用自己去计算1小时等于多少毫秒。
  2. 支持不是固定长度的间隔,比如间隔为月和年。但劣势是精度只能到秒。

它适合的任务类似于:9:00 开始执行,并且以后每周 9:00 执行一次

属性

  1. interval 执行间隔
  2. intervalUnit 执行间隔的单位(秒,分钟,小时,天,月,星期,年)

类似于 LINUX 上的任务调度命令 crontab,即利用一个包含 7 个字段的表达式来表示时间调度方式。例如,"0 15 10 * * ? *" 表示每天的 10:15AM 执行任务。对于涉及到星期和月份的调度,CronTirgger 是最适合的,甚至某些情况下是唯一选择。例如,"0 10 14 ? 3 WED" 表示三月份的每个星期三的下午 14:10PM 执行任务。

CronExpression表达式重点、难点):

由左到右按顺序代表: * * * * * * *   

  格式:   [秒] [分] [小时] [日] [月] [周] [年]

七个部分,都可以用*来表示每X。周可以省略不写

 

 

      1. DailyTimeIntervalTrigger 触发器

作用:指定每天的某个时间段内,以一定的时间间隔执行任务。并且它可以支持指定星期。

适合的任务类似于:指定每天9:00 至 18:00 ,每隔70秒执行一次,并且只要周一至周五执行。

它的属性有:

  1. startTimeOfDay 每天开始时间
  2. endTimeOfDay 每天结束时间
  3. daysOfWeek 需要执行的星期
  4. interval 执行间隔
  5. intervalUnit 执行间隔的单位(秒,分钟,小时,天,月,年,星期)
  6. repeatCount 重复次数

例子

dailyTimeIntervalSchedule()

    .startingDailyAt(TimeOfDay.hourAndMinuteOfDay(9, 0)) //天9:00开始

    .endingDailyAt(TimeOfDay.hourAndMinuteOfDay(16, 0)) //16:00 结束 

    .onDaysOfTheWeek(MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY) //周一至周五执行

    .withIntervalInHours(1) //每间隔1小时执行一次

    .withRepeatCount(100) //最多重复100次(实际执行100+1次)

.build();

dailyTimeIntervalSchedule()

    .startingDailyAt(TimeOfDay.hourAndMinuteOfDay(9, 0)) //第天9:00开始

    .endingDailyAfterCount(10) //每天执行10次,这个方法实际上根据 startTimeOfDay+interval*count 算出 endTimeOfDay

    .onDaysOfTheWeek(MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY) //周一至周五执行

    .withIntervalInHours(1) //每间隔1小时执行一次

    .build();

  1. 任务状态

quartz包中org.quartz.Trigger接口有默认任务状态:

public static enum TriggerState {
    NONE,
    NORMAL,
    PAUSED,
    COMPLETE,
    ERROR,
    BLOCKED;

    private TriggerState() {
    }
}

PAUSED 暂停 code:1

BLOCKED 阻塞 code:4

ERROR 错误 code:3

NONE 不存在 code:-1

NORMAL 正常 code:0

COMPLETE 完成 code:2

  1. 入门案例
    1. 导包

<dependency>

<groupId>org.quartz-scheduler</groupId>

<artifactId>quartz</artifactId>

<version>2.3.0</version>

</dependency>

<dependency>

<groupId>org.quartz-scheduler</groupId>

<artifactId>quartz-jobs</artifactId>

<version>2.3.0</version>

</dependency>

    1. 创建任务类

创建任务类,实现Job接口,重写execute()方法。

public class HelloJob implements Job {

@Override

public void execute(JobExecutionContext context) throws JobExecutionException {

//获取context上下文中的JobDetail对象

JobDetail jobDetail = context.getJobDetail();

//获取JobDetail对象中name性的值

String name = jobDetail.getJobDataMap().getString("name");

System.out.println(name);

}

}

注1:关注execute()方法中的context上下文参数。

注2:execute()方法主要用于根据业务需求编写业务逻辑代码,比如:发短信。

    1. 测试

@Test
public void test1(){
    try {
        //1.定义/构建 JobDetail 实例,用于定义作业的实例

        //  将HelloJob类添加到JobDetail清单
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)

.withIdentity("任务名", "任务组名")

.build();

        //2.构建Trigger实例

        //Date startTime = new Date(System.currentTimeMillis() + 3*1000L); 3秒后启动任务
        Trigger trigger = TriggerBuilder.newTrigger()

.withIdentity("触发器名", "触发器组名")

//立即启动(默认),如设为某一时间再启动使用:.startAt(statTime)

.startNow()

.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))

.build();

//3.创建scheduler调度器(Springboot可自动装配scheduler)

Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

scheduler.scheduleJob(jobDetail, trigger);

//4.启动调度器

scheduler.start();

//模拟运行一段时间后关闭真实项目不要写

Thread.sleep(20000);

//5.关闭任务调度

scheduler.shutdown(true);
} catch (Exception e) {
e.printStackTrace();
}

}

问题1:为什么设计成JobDetail + Job,不直接使用Job?

这是因为任务是有可能并发执行,如果Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题。而JobDetail & Job 方式,sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样就可以规避并发访问的问题。

  1. Quartz体系结构

   

 

每个定时任务都是一个线程,这些线程都在quartz的线程池中。

第1步:Job和Trigger必须先要注册在Scheduler中。

第2步:Scheduler调用注册的Trigger。

第3步:Trigger被唤醒,调用Job,开始执行。

一个job可以被多个Trigger 绑定,但是一个Trigger只能绑定一个job!

API:

  1. Scheduler用于与调度程序交互的主程序接口。

Scheduler调度程序-任务执行计划表,只有安排进执行计划的任务Job(通过Scheduler.scheduleJob),当它预先定义的执行时间到了的时候(任务触发Trigger),该任务才会执行。

  1. Job我们预先定义的希望在未来时间能被调度程序执行的任务类,我们可以自定义。
  2. JobDetail使用JobDetail来定义定时任务的实例,JobDetail实例是通过JobBuilder类创建的。
  3. JobDataMap可以包含不限量的(序列化的)数据对象,在Job实例执行的时候,可以使用其中的数据;JobDataMap是Java Map接口的一个实现,额外增加了一些便 于存取基本类型的数据的方法。
  4. Trigger触发器,Trigger对象是用来触发执行job的。当调度一个job时,我们实例一个触发器然后调整它的属性来满足job执行的条件。表明任务在什么时候会执行。定义了一个已经被安排的任务将会在什么时候执行的时间条件,比如每2秒就执行一次。
  5. JobBuilder用于声明一个任务实例,也可以定义关于该任务的详情,比如任务名、组名等,这个声明的实例将会作为一个实际执行的任务。
  6. TriggerBuilder触发器创建器,用于创建触发器trigger实例。
  7. JobListener、TriggerListener、SchedulerListener监听器,用于对组件的监听。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值