Quartz快速入门

一、Quartz概念

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与2EE与|2SE应用程序相结合也可以单独使用。

quartz是开源且具有丰富特性的"任务调度库",能够集成于任何的java应用,小到独立的应用,大至电子商业系统。quartz能够创建亦简单亦复杂的调度,以执行上十、上百,甚至上万的任务。任务job被定义为标准的java组件,能够执行任何你想要实现的功能。quartz调度框架包含许多企业级的特性,如JTA事务、集群的支持。

简而言之,quartz就是基于java实现的任务调度框架,用于执行你想要执行的任何任务。

Quartz运行环境

● Quartz 可以运行嵌入在另一个独立式应用程序

● Quartz 可以在应用程序服务器(或servlet容副内被实例化,并且参与事务)

● Quartz可以作为一个独立的程序运行(其自己的ava虚拟机内),可以通过RMI使用

●Quartz 可以被实例化,作为独立的项目集群(负载平衡和故障转移功能),用于作业的执行

三、Quartz设计模式

●  Builder模式

●  Factory模式

●  组件模式  JobDetail   Trigger

●  链式编程 

四.Quartz学习的核心概念

● 任务Job Ⅰ

Job就是你想要实现的任务类,每一个Job必须实现org.quartz.job接口,且只需实现接口定义的execute()方法。

●触发器Triger

Triger为你执行任务的触发器,比如你想每天定时3点发送一份统计邮件,Trigger将会设置3点进行执行该任务。Trigger主要包含两种SimpleTrigger和CronTrigger两种。关于二者的区别的使用场景,后续的课程会进行讨论。

● 调度器Scheduler

Scheduler为任务的调度器,它会将任务job及触发器Trigger整合起来,负责基于Triggeri设定的时间来执行Job.

五、Quartz的体系结构

Quartz快速入门_第1张图片

六、Quartz 的常用API

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

七、入门demo

public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        //输出当前时间
        Date date = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat();
        String dateString = dateFormat.format(date);
        //工作内容
        System.out.println("正在进行数据库的备份工作,备份数据库的时间是"+dateString);
    }
}
public class HelloSchedulerDemo {
    public static void main(String[] args) throws Exception {
        // 1.调度器(Scheduler),从工厂获取调度实例
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        //2.任务实例(JobDetail)
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)  //加载任务类,与HelloJob完成绑定,要求HelloJob实现Job接口
                .withIdentity("job1", "group1")  //参数1:任务的名称(唯一实例),参数2:任务组的名称
                .build();
        //3.触发器(Trigger)
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group2") //参数1:触发器的名称(唯一实例),参数2:触发器组的名称
                .startNow() //马上启动触发器
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5))
                .build();
        //让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
        scheduler.scheduleJob(jobDetail,trigger);
        //启动
        scheduler.start();
    }
}

集群模式-Demo

/**
 * 调度器
 */
@Configuration
public class SchedulerConfig {
    @Autowired
    private DataSource dataSource;
    @Bean
    public Scheduler scheduler() throws IOException {
         return schedulerFactoryBean().getScheduler();
    }

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setSchedulerName("cluster_scheduler");
        factory.setDataSource(dataSource);
        factory.setApplicationContextSchedulerContextKey("application");
        factory.setQuartzProperties(quartzProperties());  //数据源
        factory.setTaskExecutor(schedulerThreadPool()); //线程池
        factory.setStartupDelay(0);  //是否立即执行
        return factory;
    }

    /**
     * 数据源
     * @return
     * @throws IOException
     */
    @Bean
    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("/spring-quartz.properties"));
        propertiesFactoryBean.afterPropertiesSet();
        return propertiesFactoryBean.getObject();
    }

    /**
     * 线程池
     * @return
     */
    @Bean
    public Executor schedulerThreadPool(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
        executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors());
        executor.setQueueCapacity(Runtime.getRuntime().availableProcessors());
        return executor;
    }
}
@Component
public class StartApplicationListener implements ApplicationListener {
    @Autowired
    private Scheduler scheduler;

    /**
     * 触发器
     * @param contextRefreshedEvent
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        /**
         * 集群模式
         */
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey("trigger1","group1");
            Trigger trigger = scheduler.getTrigger(triggerKey);
            if(trigger == null ){
                trigger = TriggerBuilder.newTrigger()
                        .withIdentity(triggerKey)                     // cron表达式  日期
                        .withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?"))
                        .startNow()
                        .build();
                JobBuilder jobBuilder = JobBuilder.newJob(QuartzJob.class);
                jobBuilder.withIdentity("job1", "group1");
                JobDetail jobDetail = jobBuilder
                        .build();
                scheduler.scheduleJob(jobDetail,trigger);

            }
            TriggerKey triggerKey2 = TriggerKey.triggerKey("trigger2","group2");
            Trigger trigger2 = scheduler.getTrigger(triggerKey2);
            if(trigger2 == null ) {
                trigger2 = TriggerBuilder.newTrigger()
                        .withIdentity(triggerKey2)                     // cron表达式  日期
                        .withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?"))
                        .startNow()
                        .build();
                JobBuilder jobBuilder = JobBuilder.newJob(QuartzJob.class);
                jobBuilder.withIdentity("job2", "group2");
                JobDetail jobDetail2 = jobBuilder
                        .build();
                scheduler.scheduleJob(jobDetail2, trigger2);
                scheduler.start();
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}
  • 八、Job和JobDetail 介绍

●Job∶工作任务调度的接口,任务类需要实现该接口。该接口中定义execute方法,类似DK提供的TimeTask类的run方法。在里面编写任务执行的业务逻辑。

● Job实例在Quartz中的生命周期∶每次调度器执行Job时,它在调用execute方法前会创建一个新的Job实例,当调用完成后,关联的Job对象实例会被释放,释放的实例会被垃圾回收机制回收。

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

● JobDetail重要属性∶name、group、jobClasS、jobDataMap

九、SpringBoot 整合 Quartz

1、引入依赖


    xin.altitude.cms
    ucode-cms-quartz
    1.5.3.1

2、快速上手

实现 org.quartz.Job接口;

使用@CronExp添加任务的调度策略;使用注解 Component将任务注入容器中。

启动项目,定时任务便处于监听与运行中。

@Component
@DisallowConcurrentExecution
@CronExp(id=1,cron = "0/5 * * * * ?")   //每5秒运行一次
public class Demo1Job implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("任务1:"+ LocalDateTime.now());
    }
}

3、手动触发定时任务

定时任务除了以既有频率周期性运行外,还有通过接口手动被触发的能力。

调用如下接口,可手动触发任务ID编号为jobId 的任务。

http://Localhost:8080/cms-api/quartz/job/{obId}

如果有手动触发定时任务的需求,则需要任务ID 唯一并 且 已知,因此需要在编写定时任务时手动指定。

@CronExp(id = 1,cron ="0/5****?")

通过注解CronExp的id属性可指定任务ID,不显示指定则使用随机ID,不满足已知的条件,因此无法手动触发。

4、带参数任务

尽管大多数任务不需要注入参数,但仍有少量的场景需要向定时任务注入参数。

public void execute(JobExecutionContext context){
/* 如果在调用任务时传入了参数,则能够从Map中获取 */
Map dataMap = context.gethergedJobDataMap();/* 比如从Map中获取一个键值对,一般来说参数均为基本数据类型 */Object key = dataMap.get("key");
System.out.println("任务2∶"+ LocaLDateTime.now()+"∶"+ key);
}

在编写定时任务时,可从 JobExecutionContext 对象中解析一个Map,从而完成参数的注入。

http://localhost:8080/cms-api/quartz/job/1?key=a

上述http调用的含义是手动触发任务ID为【1】的任务,并且向其传递参数为【key】值为【a】的参数。

5、任务并发

本框架不支持任务并发,换句话说并发对定时任务不利,因此需要手动禁止。

需要注意的是 Quartz 的并发是指当任务执行耗时超过任务调度周期时,上一个任务未执行完,新任务是否执行。

一般来说需要显示禁止并发,在任务类上添加注解@DisallowConcurrentExecution即可禁止任务并发。

你可能感兴趣的:(java)