定时任务调度框架Quartz

1.前言

最近须要写一个天天定点自动执行的定时任务,对于之前本身写小项目,可能会选择java自带的Timer类,可是对于公司中的项目,Timer类实现定时任务只能有一个后台线程执行任务,而且只能让程序按照某个频度执行,并不能在指定时间点执行。同时因为通常大型项目会有多个job分别须要在不一样的时间点自动执行,单线程的Timer知足不了需求。而任务调度框架Quartz恰好知足这些需求,我是实习公司的项目中就已经配置好了这个框架,在使用的时候了解了这个quartz,现总结以下。html

2.quartz是什么?

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,彻底由Java开发,能够用来执行定时任务,相似于java.util.Timer。可是相较于Timer, Quartz增长了不少功能:

持久性做业 - 就是保持调度定时的状态;
做业管理 - 对调度做业进行有效的管理; 
复制代码

quartz主要包括三个部分的模块,定时任务Job、触发器Trigger、调度器Scheduler。
简单的流程就是:调度器根据触发器来执行任务。java

2.1 Job

  • Job:即被调度的任务,实现业务逻辑的接口,接口中只有一个方法execute。若是须要建立一个任务,必须得实现这个接口,任务每次被调用的时候都会执行这个execute方法的逻辑。
//预约义Job接口--pom.xml须要加载依赖的包
public interface Job {
    void execute(JobExecutionContext var1) throws JobExecutionException;
}

//任务逻辑经过实现Job接口,而且重写execute方法,将须要执行的逻辑实现写在execute函数中
public class TestJob implements Job {
      /**把要执行的操做,写在execute方法中 */
      @Override
      public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
          SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
          System.out.println("I can do something...");
          System.out.println(sdf.format(new Date()));
      }
  }
 
参考:https://juejin.im/post/5a0c08c5f265da4335624c8f

//1. 建立一个JodDetail实例 将该实例与Hello job class绑定 (链式写法)
JobDetail jobDetail = JobBuilder.newJob(QuartzOrderReturn.class)// 定义Job类为QuartzOrderReturn类,这是真正的执行逻辑所在
                    .withIdentity(StrUtils.uuid())
                    .setJobData(jobDataMap)
                    .build();
复制代码

2.2 Trigger

  • trigger:触发器,用于定义任务调度的时间 规则。quartz有两种常见的trigger:SimpleTrigger和CornTrigger。
  • SimpleTrigger 主要针对一些简单的时间触发进行配置使用,好比指定的时间开始而后在必定的时间间隔以内重复执行一个job,同时能够任意指定重复的次数,可是不适合天天定时执行任务的场景,好比SimpleTrigger解决不了须要天天半夜十二点执行的任务。
//建立SimpleTrigger startTime end 分别表示触发器的首次触发时间和再也不被触发的时间
 SimpleTrigger trigger = newTrigger()
                    .withIdentity("trigger", "group")
                    .startAt(startTime)
                    .endAt(end)
                    .withSchedule(
                            simpleSchedule().withIntervalInHours(
                                    executeFrequency).withRepeatCount(
                                    executeBatch)).build();
复制代码
  • CronTrigger 能够配置更复杂的触发时刻表,基于日历的做业触发器。使用SimpleTrigger触发器须要设置不一样的属性支撑,代码编写比较多,而且不灵活。而CornTrigger就比较灵活,能够经过设计Corn表达式来控制复杂的触发时间表。算法

  • Corn表达式:用于配置CornTrigger实例,由7个表达式组成的字符串,描述了时间表的详细信息。bash

//建立CornTrigger 好比下面的就是设计天天中午十二点整触发
CronTrigger trigger = newTrigger().withIdentity(triggerKey).withSchedule(cronSchedule("0 0 12 * * ?")).build();
// 把job和trigger加入调度
scheduler.scheduleJob(jobDetail, trigger);
//启动scheduler
scheduler.start();

复制代码

对于Corn的理解参考: mp.weixin.qq.com/s/-0kwxC2lz…?
在线生成Corn表达式: cron.qqe2.com/

2.3 Scheduler

  • Scheduler:在每次调度器执行job时,它在调用execute方法前会建立一个新的job实例,当调用完成以后,关联的job对象实例会被释放,释放的实例会被垃圾回收机制回收。
  • Scheduler能够将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job能够对应多个Trigger,但一个Trigger只能对应一个Job。
// 把job和trigger加入调度
scheduler.scheduleJob(jobDetail, trigger);
//启动scheduler
scheduler.start();
复制代码

//一个完整的实例
private void quartzOrderReturn(List<String> returnIds) {
        try {
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
            JobDataMap jobDataMap = new JobDataMap();
            jobDataMap.put("returnIds", returnIds);
            //1. 建立一个JodDetail实例 将该实例与Hello job class绑定 (链式写法)
            JobDetail jobDetail = JobBuilder.newJob(QuartzOrderReturn.class)// 定义Job类为QuartzOrderReturn类,这是真正的执行逻辑所在
                    .withIdentity(StrUtils.uuid())
                    .setJobData(jobDataMap)
                    .build();
            // 2. 定义一个Trigger,定义该job在10秒后执行,而且执行一次
            Date startTime = new Date();
            startTime.setTime(startTime.getTime() + 10000L);
            SimpleTrigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(StrUtils.uuid(), HeaderNameConstants.getQuartzTrigger()).startNow()// 定义名字和组
                    .startAt(startTime)
                    .withSchedule(SimpleScheduleBuilder
                            .simpleSchedule()
                            .withIntervalInSeconds(2)//定义时间间隔是2秒
                            .withRepeatCount(0)//定义重复执行次数是无限次
                    )
                    .build();
            // 4. 将trigger和jobdetail加入这个调度
            scheduler.scheduleJob(jobDetail, trigger);
            // 5. 启动scheduler
            scheduler.start();
            // 6. 任务执行后20秒后休眠
            Thread.sleep(startTime.getTime() + 20000L);
            // 7. 若是定时任务开启则关闭
            if (scheduler.isStarted()) {
                scheduler.shutdown(true);
            }
        } catch (SchedulerException | InterruptedException e) {
            e.printStackTrace();
        }
    }
复制代码

2.4 结构图

  • IJob:预实现的Job接口
  • MyJob:实现Job接口,定时任务逻辑写再execute函数
  • JobDetail,顾名思义,就是表示关于每一个Job的相关信息,它主要包括两个核心组件,即Job Task和JobData Map
  • Trigger,表示触发器,根据配置规则来触发执行计划调度job,它主要包括两个核心组件,即SimpleTrigger和CronTrigger
  • IJobStore,表述任务存储器,主要存储job和trigger相关信息。
  • ISchedulerFactory,表示任务计划工厂,用来管理任务计划IScheduler。
  • IScheduler,表述任务计划,它至关于一个容器,具体job和job相关trigger就可以被注入其中,从而实现任务计划调度。其主要经常使用的方法:Start --启动执行计划 Shutdowm --关闭执行计划
//每一个2秒执行一次
            string cronParam = "*/2 * * * * ?";
            //建立计划任务抽象工厂
            ISchedulerFactory sf = new StdSchedulerFactory();
            //建立计划任务
            IScheduler sched = sf.GetScheduler();
            //建立job
            JobDetail job = new JobDetail("myJob","group", typeof(MyJob));
            //建立触发器
            Trigger trigger = new CronTrigger("myTrigger","group",cronParam);
            //将job和trigger注入到计划任务中
            sched.ScheduleJob(job, trigger);
            //启动计划任务
            sched.Start();
            //关闭计划任务
            //sched.Shutdown();
复制代码

参考: www.cnblogs.com/wangjiming/…

3.定时任务如何实现并调度:TimeWheel

定时通常是使用时间轮(time wheel)算法实现。 时间轮算法简单来讲能够用下图表示,其主体是一个循环列表,框架

新任务加入时,会根据目前指针所在位置和须要等待的时间,肯定保存在时钟的哪一个位置。时间轮有3个重要的属性参数,ticksPerWheel(一轮的tick数),tickDuration(一个tick的持续时间)以及 timeUnit(时间单位)。
例如 当ticksPerWheel=60,tickDuration=1,timeUnit=秒,这就和现实中秒针走动彻底相似了,咱们就用这种状况举例子。箭头运行到一个位置时,就运行相应的任务,而后经过sleep将时间补足一秒,正好就开始下一个tick了。这样循环往复,就可让每一个任务在须要的时间执行。

参考连接:www.zhihu.com/question/41… www.kafka.cc/archives/24…ide

4.项目中的扩展

1.抽象封装Job类函数

2.页面管理多个定时任务post

5.总结

  • 理解quartz是什么,包括哪些部分?
  • 理解quartz是如何实现定时任务的?
  • 理解quartz是如何管理调度多个定时任务的?