Spring中对开源调度作业框架Quartz的使用

        Quartz是由Java语言编写的开源作业调度框架,它可以用来创建简单或为运行十个、百个甚至是好几万个Jobs这样复杂的程序,包含调度器监听、作业和触发器监听,允许通过时间间隔来调度作业。

        有几个概念需要清楚一下:

        - Job

           任务,表示一个要执行具体内容的工作(任务内容)

        - JobDetail

          任务的细节,表示一个具体的可执行的调度程序,用于组合Job(任务方案,名称,策略)

        - Trigger

          触发器,执行任务的规则,代表一个调度参数的配置,用于组合JobDetail(什么时间与间隔调用)

       - Scheduler

         任务调度,代表一个任务调度器,用于组合Trigger

        

         1. 下载

         这里下载主要是想拿它的数据库初始化脚本而已,后面使用它主要基于maven依赖的方式。

         

            部分内容如下:

            

           主要是初始化Quartz框架需要用到的表。

         2. Spring Boot项目集成

          

          对于原有项目,也可以如下直接集成:

          

          这里使用的MySQL数据库:

          

         3. 增加quartz配置文件(resources/quartz.properties)和配置类

             

#使用自己的配置文件
org.quartz.jobStore.useProperties:true

#默认或是自己改名字都行
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
#如果使用集群,instanceId必须唯一,设置成AUTO
org.quartz.scheduler.instanceId = AUTO

org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true

#存储方式使用JobStoreTX,也就是数据库
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#是否使用集群(如果项目只部署到 一台服务器,就不用了)
#org.quartz.jobStore.isClustered = true
#org.quartz.jobStore.clusterCheckinInterval=20000
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.dataSource = myDS

#配置数据源
#数据库中quartz表的表名前缀

org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://xxxx:xx/my_test?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=round&allowMultiQueries=true
org.quartz.dataSource.myDS.user = xxx
org.quartz.dataSource.myDS.password = xxxxxx
org.quartz.dataSource.myDS.maxConnections = 5

        配置类如下:

        

package cn.linjk.quartz.config;

import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;

@Configuration
public class QuartzConfig {

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


    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) throws IOException {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();

        schedulerFactoryBean.setOverwriteExistingJobs(true);
        schedulerFactoryBean.setQuartzProperties(quartzProperties());

        return schedulerFactoryBean;
    }
}

         4. 作业调度测试

             流程如下:

            

                 第一步,可以由controller接口调用,也可以由其他service类调用触发

                 第二步,通常作为service:

                 

package cn.linjk.quartz.service;

import java.util.List;

import cn.linjk.quartz.dao.ICSchedulerTrigger;
import cn.linjk.quartz.domain.CScheduleTrigger;
import org.quartz.*;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ScheduleTriggerService {
    private static final Logger logger = LoggerFactory.getLogger(ScheduleTriggerService.class);
    @Autowired private Scheduler scheduler;
    @Autowired private ICSchedulerTrigger triggerRepository;

    // 简单触发器,通常用于间隔时间短执行指定任务,可以指定重复次数后取消任务的周期执行
    public void simpleTrigger() {
        try {
            CScheduleTrigger scheduleTrigger = triggerRepository.getSimpleTrigger();
            if (scheduleTrigger != null) {
                Integer status = scheduleTrigger.getStatus();
                TriggerKey triggerKey = TriggerKey.triggerKey(scheduleTrigger.getJobName(), scheduleTrigger.getJobGroup());
                SimpleTrigger simpleTrigger = (SimpleTrigger) scheduler.getTrigger(triggerKey);
                if (null == simpleTrigger) {
                    if (status == 0) {
                        // 任务状态为0,即禁用状态,不执行,退出
                        return;
                    }
                    String gap = scheduleTrigger.getCron();
                    if (gap.contains("S")) {
                        gap = gap.substring(0, gap.indexOf("S"));
                    }

                    JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(scheduleTrigger.getJobName()))
                            .withIdentity(scheduleTrigger.getJobName(), scheduleTrigger.getJobGroup()).build();
                    JobDataMap jobDataMap = jobDetail.getJobDataMap();
                    jobDataMap.put("param_1", "param1");
                    // 一个scheduler 可以绑定多个job
                    // 一个 Job 在同一个 Scheduler 实例中通过名称和组名能唯一被标识
                    // 任务被配置为每5秒执行一次,共执行四次(注意参数为3)
                    SimpleScheduleBuilder builder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(Integer.valueOf(gap));
                    builder.withRepeatCount(3);
                    Trigger trigger = TriggerBuilder.newTrigger().withIdentity(scheduleTrigger.getJobName(), scheduleTrigger.getJobGroup()).startNow()
                            .withSchedule(builder).build();
                    // 执行4次后,需要可以重新循环执行时,需要如下语句,否则执行完,再次触发周期任务无效
                    trigger = trigger.getTriggerBuilder().withIdentity(scheduleTrigger.getJobName(), scheduleTrigger.getJobGroup()).build();
                    scheduler.scheduleJob(jobDetail, trigger);
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    // cron任务,指定时间周期执行,格式与linux的cron任务一样
    public void refreshTrigger() {
        try {
            //查询出数据库中所有的定时任务
            List<CScheduleTrigger> jobList = triggerRepository.findAll();
            if (jobList != null) {
                for (CScheduleTrigger scheduleJob : jobList) {
                    Integer status = scheduleJob.getStatus(); //该任务触发器目前的状态
                    TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
                    CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
                    //说明本条任务还没有添加到quartz中
                    if (null == trigger) {
                        if (status == 0) { //如果是禁用,则不用创建触发器
                            continue;
                        }

                        JobDetail jobDetail = null;
                        try {
                            //创建JobDetail(数据库中job_name存的任务全路径,这里就可以动态的把任务注入到JobDetail中)
                            jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(scheduleJob.getJobName())).withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).build();

                            //表达式调度构建器
                            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCron());
                            ///设置定时任务的时间触发规则
                            trigger = TriggerBuilder.newTrigger().withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).withSchedule(scheduleBuilder).build();
                            //把trigger和jobDetail注入到调度器
                            scheduler.scheduleJob(jobDetail, trigger);
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }

                    } else {  //说明查出来的这条任务,已经设置到quartz中了
                        // Trigger已存在,先判断是否需要删除,如果不需要,再判定是否时间有变化
                        if (status == 0) { //如果是禁用,从quartz中删除这条任务
                            JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
                            scheduler.deleteJob(jobKey);
                            continue;
                        }
                        String searchCron = scheduleJob.getCron(); //获取数据库的
                        String currentCron = trigger.getCronExpression();
                        if (!searchCron.equals(currentCron)) {  //说明该任务有变化,需要更新quartz中的对应的记录
                            //表达式调度构建器
                            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(searchCron);

                            //按新的cronExpression表达式重新构建trigger
                            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

                            //按新的trigger重新设置job执行
                            scheduler.rescheduleJob(triggerKey, trigger);
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error("定时任务刷新触发器任务异常,异常信息:", e);
        }
    }
}

                 第三步,数据库增加配置表,配置定时任务信息:

                 

                 记录1是cron表达式的任务

                记录2是周期间隔任务,配置为6秒执行一次,即: 6 seonds per-time

                job_name是java类的路径,在第二步的service会利用反射找到执行的类,执行对应的业务逻辑。

                第四步,业务任务类实现,路径名对应数据库表配置:

                

package cn.linjk.quartz.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SimpleTrigger;
import org.springframework.stereotype.Component;

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

@Component
public class MySimpleTask implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        try {
            //可以通过context拿到执行当前任务的quartz中的很多信息,如当前是哪个trigger在执行该任务
            SimpleTrigger trigger = (SimpleTrigger) context.getTrigger();
            String jobName = trigger.getKey().getName();
            String jobGroup = trigger.getKey().getGroup();
            System.out.println("jobName:"+jobName);
            System.out.println("jobGroup:"+jobGroup);
        }catch (Exception e){
            e.printStackTrace();
        }
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("间隔任务执行任务中 " + simpleDateFormat.format(new Date()));
    }
}

         在间隔任务中,我如下直接获取指定的任务:

         

        5. 运行测试,可以增加一个controller调用,类似如下:

            

            调用后,会看到调度器在执行,此时,即使重启该微服务,重启后,不需要重新触发任务执行,quartz框架会自动从数据库表中获取配置,重新执行周期任务,除非改了任务的禁用或启用状态。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值