学习笔记_定时框架Quartz

学习笔记_Quartz

1、什么是Quartz

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer。但是相较于Timer, Quartz增加了很多功能:

  • 持久性作业 - 就是保持调度定时的状态;
  • 作业管理 - 对调度作业进行有效的管理;

大部分公司都会用到定时任务这个功能。
拿火车票购票来说,当你下单后,后台就会插入一条待支付的task(job),一般是30分钟,超过30min后就会执行这个job,去判断你是否支付,未支付就会取消此次订单;当你支付完成之后,后台拿到支付回调后就会再插入一条待消费的task(job),Job触发日期为火车票上的出发日期,超过这个时间就会执行这个job,判断是否使用等。

在我们实际的项目中,当Job过多的时候,肯定不能人工去操作,这时候就需要一个任务调度框架,帮我们自动去执行这些程序。那么该如何实现这个功能呢?

  1. 首先我们需要定义实现一个定时功能的接口,我们可以称之为Task(或Job),如定时发送邮件的task(Job),重启机器的task(Job),优惠券到期发送短信提醒的task(Job),实现接口如下:

    img
  2. 有了任务之后,还需要一个能够实现触发任务去执行的触发器,触发器Trigger最基本的功能是指定Job的执行时间,执行间隔,运行次数等。

img
  1. 有了Job和Trigger后,怎么样将两者结合起来呢?即怎样指定Trigger去执行指定的Job呢?这时需要一个Schedule,来负责这个功能的实现。

img

上面三个部分就是Quartz的基本组成部分:

  • 调度器:Scheduler
  • 触发器:Trigger,包括SimpleTrigger和CronTrigger
  • 任务:JobDetail

2、Quartz Demo搭建

下面来利用Quartz搭建一个最基本的Demo。
1、导入依赖的jar包:

<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.2</version>
</dependency>

2、新建一个能够打印任意内容的Job:

package top.saodisheng.quartz.task;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

/**
 * Description: 定时打印任务
 * @author 扫地生_saodisheng
 * @date 2021/07/05
 */
public class PrintWordsJob implements Job {
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        String printTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date());
        System.out.println("PrintWordsJob start at : " + printTime
                                   + ", prints: Hello Job" + new Random().nextInt(100));
    }
}

3、创建Schedule,执行任务:

package top.saodisheng.quartz.sheduler;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import top.saodisheng.quartz.task.PrintWordsJob;

import java.util.concurrent.TimeUnit;

/**
 * Description: 调度器
 * @author 扫地生_saodisheng
 * @date 2021/07/05
 */
public class MySheduler {
    public static void main(String[] args) throws SchedulerException, InterruptedException {
        // 1. 创建调度器Scheduler
        StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();

        // 2. 创建JobDetail实例,并与定时job PrintWordJob类绑定(Job执行内容)
        JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class).withIdentity("job1", "group1").build();

        // 3. 创建Trigger实例,每秒执行一次
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
                                      .startNow()// 立即生效
                                      .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                                                            .withIntervalInSeconds(1)// 每秒执行一次
                                                            .repeatForever())// 一直执行
                                      .build();

        // 4. 执行
        scheduler.scheduleJob(jobDetail, trigger);
        System.out.println("--------------scheduler start-----------------");
        scheduler.start();

        // 睡眠
        TimeUnit.MINUTES.sleep(1);
        scheduler.shutdown();
        System.out.println("--------------scheduler shutdown-----------------");
    }
}

运行程序,可以看到程序每隔1s会打印出内容,且在一分钟后结束:

image-20210706100057763

image-20210706100117042

3、Quartz核心详解

下面就程序中出现的几个参数,看一下Quartz框架中的几个重要参数:

Job和JobDetail
JobExecutionContext
JobDataMap
Trigger、SimpleTrigger、CronTrigger

1、Job和JobDetail
Job是Quartz中的一个接口,接口下只有execute方法,在这个方法中编写业务逻辑。
接口中的源码:

image-20210706100414714

JobDetail用来绑定Job,为Job实例提供许多属性:

name
group
jobClass
jobDataMap

JobDetail绑定指定的Job,每次Scheduler调度执行一个Job的时候,首先会拿到对应的Job,然后创建该Job实例,再去执行Job中的execute()的内容,任务执行结束后,关联的Job对象实例会被释放,且会被JVM GC清除。

为什么设计成JobDetail + Job,不直接使用Job

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

2、JobExecutionContext
JobExecutionContext中包含了Quartz运行时的环境以及Job本身的详细数据信息
当Schedule调度执行一个Job的时候,就会将JobExecutionContext传递给该Job的execute()中,Job就可以通过JobExecutionContext对象获取信息。
主要信息有:

image-20210706100824095

image-20210706100856406

3、JobDataMap
JobDataMap实现了JDK的Map接口,可以以Key-Value的形式存储数据。
JobDetail、Trigger都可以使用JobDataMap来设置一些参数或信息,
Job执行execute()方法的时候,JobExecutionContext可以获取到JobExecutionContext中的信息:
如:

JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class)
    .usingJobData("jobDetail1", "这个Job用来测试的")
    .withIdentity("job1", "group1")
    .build();

 Trigger trigger = TriggerBuilder.newTrigger()
     .withIdentity("trigger1", "triggerGroup1")
     .usingJobData("trigger1", "这是jobDetail1的trigger")
     .startNow()//立即生效
     .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                   .withIntervalInSeconds(1)//每隔1s执行一次
                   .repeatForever())
     .build();//一直执行

Job执行的时候,可以获取到这些参数信息:

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {        
    String printTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date());        
    System.out.println("PrintWordsJob start at : " 
                       + printTime                                   
                       + ", prints: Hello Job" 
                       + new Random().nextInt(100));    
}

4、Trigger、SimpleTrigger、CronTrigger

  • Trigger

Trigger是Quartz的触发器,会去通知Scheduler何时去执行对应Job。

new Trigger().startAt():表示触发器首次被触发的时间;
new Trigger().endAt():表示触发器结束触发的时间;

  • SimpleTrigger

SimpleTrigger可以实现在一个指定时间段内执行一次作业任务或一个时间段内多次执行作业任务
下面的程序就实现了程序运行5s后开始执行Job,执行Job 5s后结束执行:

Date startDate = new Date();
startDate.setTime(startDate.getTime() + 5000);

Date endDate = new Date();
endDate.setTime(startDate.getTime() + 5000);
        
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")                
    .usingJobData("trigger1", "这是jobDetail1的trigger")                
    .startNow()//立即生效                
    .startAt(startDate)                
    .endAt(endDate)                
    .withSchedule(SimpleScheduleBuilder.simpleSchedule()                
                  .withIntervalInSeconds(1)//每隔1s执行一次                
                  .repeatForever())
    .build();//一直执行
  • CronTrigger

CronTrigger功能非常强大,是基于日历的作业调度,而SimpleTrigger是精准指定间隔,所以相比SimpleTrigger,CroTrigger更加常用。CroTrigger是基于Cron表达式的,先了解下Cron表达式:
由7个子表达式组成字符串的,格式如下:

[秒] [分] [小时] [日] [月] [周] [年]

Cron表达式的语法比较复杂,
如:* 30 10 ? * 1/5 *
表示(从后往前看)
[指定年份] 的[ 周一到周五] [指定月] [不指定日] [上午10时] [30分] [指定秒]

又如:00 00 00 ? * 10,11,12 1#5 2018
表示2018年10、11、12月的第一周的星期五这一天的0时0分0秒去执行任务。

下面是给的一个例子:

image-20210706101915652

可通过在线生成Cron表达式的工具:https://qqe2.com/cron来生成自己想要的表达式。

img

下面的代码就实现了每周一到周五上午10:30执行定时任务

package top.saodisheng.quartz.sheduler;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import top.saodisheng.quartz.task.PrintWordsJob;

import java.util.Date;

/**
 * Description:
 *
 * @author 扫地生_saodisheng
 * @date 2021/07/05
 */
public class MyScheduler2 {
    public static void main(String[] args) throws SchedulerException, InterruptedException {
        // 1、创建调度器Scheduler
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        // 2、创建JobDetail实例,并与PrintWordsJob类绑定(Job执行内容)
        JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class)
                                      .usingJobData("jobDetail1", "这个Job用来测试的")
                                      .withIdentity("job1", "group1").build();
        // 3、构建Trigger实例,每隔1s执行一次
        Date startDate = new Date();
        startDate.setTime(startDate.getTime() + 5000);

        Date endDate = new Date();
        endDate.setTime(startDate.getTime() + 5000);

        CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                                          .withIdentity("trigger1", "triggerGroup1")
                                          .usingJobData("trigger1", "这是jobDetail1的trigger")
                                          .startNow()//立即生效
                                          .startAt(startDate)
                                          .endAt(endDate)
                                          .withSchedule(CronScheduleBuilder.cronSchedule("* 30 10 ? * 1/5 2018"))
                                          .build();
        //4、执行
        scheduler.scheduleJob(jobDetail, cronTrigger);
        System.out.println("--------scheduler start ! ------------");
        scheduler.start();
        System.out.println("--------scheduler shutdown ! ------------");
    }
}

4、参考文章

参考文章1

参考文章2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现定时任务可以使用Quartz框架,结合Spring MVC进行整合。下面是具体步骤: 1. 引入依赖 在pom.xml中添加依赖: ``` <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.0</version> </dependency> ``` 2. 配置Quartz 在Spring的配置文件中添加Quartz的配置,如下: ``` <!--配置Quartz--> <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="quartzProperties"> <props> <prop key="org.quartz.scheduler.instanceName">QuartzScheduler</prop> <prop key="org.quartz.scheduler.instanceId">AUTO</prop> <prop key="org.quartz.scheduler.skipUpdateCheck">true</prop> <prop key="org.quartz.scheduler.jmx.export">true</prop> <prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop> <prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.StdJDBCDelegate</prop> <prop key="org.quartz.jobStore.dataSource">myDS</prop> <prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop> <prop key="org.quartz.jobStore.isClustered">false</prop> <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop> <prop key="org.quartz.threadPool.threadCount">10</prop> <prop key="org.quartz.dataSource.myDS.driver">com.mysql.jdbc.Driver</prop> <prop key="org.quartz.dataSource.myDS.URL">jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true</prop> <prop key="org.quartz.dataSource.myDS.user">root</prop> <prop key="org.quartz.dataSource.myDS.password">123456</prop> <prop key="org.quartz.dataSource.myDS.maxConnections">30</prop> </props> </property> <property name="autoStartup" value="true"/> <property name="startupDelay" value="5"/> </bean> ``` 这里配置了Quartz的数据源,使用的是MySQL数据库。 3. 编写定时任务 创建一个类,实现Quartz的Job接口,如下: ``` public class MyJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("定时任务执行了"); } } ``` 4. 配置定时任务 在Spring的配置文件中添加对定时任务的配置,如下: ``` <!-- 配置定时任务 --> <bean id="myJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <property name="jobClass" value="com.example.MyJob"/> </bean> <bean id="myJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail" ref="myJobDetail"/> <property name="cronExpression" value="0/5 * * * * ?"/> </bean> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="myJobTrigger"/> </list> </property> <property name="schedulerContextAsMap"> <map> <entry key="key1" value="value1"/> <entry key="key2" value="value2"/> </map> </property> <property name="schedulerListeners"> <list> <bean class="org.springframework.scheduling.quartz.JobListenerFactoryBean"> <property name="name" value="jobListener"/> </bean> </list> </property> </bean> ``` 这里配置了一个每5秒执行一次的定时任务。 5. 启动定时任务 在启动Spring的时候,定时任务自动启动。也可以在代码中手动启动定时任务,如下: ``` @Autowired private Scheduler scheduler; /** * 启动定时任务 */ public void startJob() throws SchedulerException { scheduler.start(); } ``` 至此,一个使用Spring MVC和Quartz实现的定时任务就完成了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值