美文网首页微服务springboot
springboot 之集成quartz

springboot 之集成quartz

作者: xuefly3 | 来源:发表于2019-12-23 14:52 被阅读0次

    前言

    一直没机会做spring生态圈的框架,公司选择的是一些小众的微服务,鉴于此考虑,丰富自己的技术栈,花了两天时间从网上各网站上学习了springboot一些基础知识。
    本章只介绍springboot微服务集成quartz,用于项目中用到的一些定时任务,调度任务框架。

    环境准备

    • IntelliJ IDEA
    • 前一章中搭建的微服务框架

    开始集成

    1. pom.xml中增加依赖包


      依赖包.png
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-quartz</artifactId>
            </dependency>
    
    1. quartz的使用分为两种类型,一种为服务启动时定时执行任务,另一种为服务启动后,通过某些操作控制的任务(可以通过操作对其进行停止,删除,启动...)
      2.1.1 先说第一种:


      EnableScheduling.png

      在入口类DemoApplication中启用调度任务:增加注解@EnableScheduling
      2.1.2 在demo包下新建schedule包,用于存放调度任务相关类,在schedule包下新建TestSchedule类:


      TestSchedule.png
    package com.example.demo.schedule;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * 类功能描述:<br>
     * <ul>
     * <li>类功能描述1<br>
     * <li>类功能描述2<br>
     * <li>类功能描述3<br>
     * <li>#cron 的表达式:
     *(1)0 0 2 1 * ? *   表示在每月的1日的凌晨2点调整任务
     *
     *(2)0 15 10 ? * MON-FRI   表示周一到周五每天上午10:15执行作业
     *
     *(3)0 15 10 ? 6L 2002-2006   表示2002-2006年的每个月的最后一个星期五上午10:15执行作
     *
     *(4)0 0 10,14,16 * * ?   每天上午10点,下午2点,4点
     *
     *(5)0 0/30 9-17 * * ?   朝九晚五工作时间内每半小时
     *
     *(6)0 0 12 ? * WED    表示每个星期三中午12点
     *
     *(7)0 0 12 * * ?   每天中午12点触发
     *
     *(8)0 15 10 ? * *    每天上午10:15触发
     *
     *(9)0 15 10 * * ?     每天上午10:15触发
     *
     *(10)0 15 10 * * ? *    每天上午10:15触发
     *
     *(11)0 15 10 * * ? 2005    2005年的每天上午10:15触发
     *
     *(12)0 * 14 * * ?     在每天下午2点到下午2:59期间的每1分钟触发
     *
     *(13)0 0/5 14 * * ?    在每天下午2点到下午2:55期间的每5分钟触发
     *
     *(14)0 0/5 14,18 * * ?     在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
     *
     *(15)0 0-5 14 * * ?    在每天下午2点到下午2:05期间的每1分钟触发
     *
     *(16)0 10,44 14 ? 3 WED    每年三月的星期三的下午2:10和2:44触发
     *
     *(17)0 15 10 ? * MON-FRI    周一至周五的上午10:15触发
     *
     *(18)0 15 10 15 * ?    每月15日上午10:15触发
     *
     *(19)0 15 10 L * ?    每月最后一日的上午10:15触发
     *
     *(20)0 15 10 ? * 6L    每月的最后一个星期五上午10:15触发
     *
     *(21)0 15 10 ? * 6L 2002-2005   2002年至2005年的每月的最后一个星期五上午10:15触发
     *
     *(22)0 15 10 ? * 6#3   每月的第三个星期五上午10:15触发<br>
     * </ul>
     * 修改记录:<br>
     * <ul>
     * <li>修改记录描述1<br>
     * <li>修改记录描述2<br>
     * <li>修改记录描述3<br>
     * </ul>
     *
     * @author xuefl
     * @version 5.0 since 2019-12-19
     */
    @Component
    @Slf4j
    public class TestSchedule {
    
        @Scheduled(fixedRate = 10 * 1000, initialDelay = 5000)  //采用间隔调度,每10秒执行一次
        public void runJoba(){ //定义一个执行的任务
            log.info("[*******MyTaskA -- 间隔调度 ******]"+
                    new SimpleDateFormat("yyy-MM-dd HH:mm:ss.SSS").format(new Date()));
        }
    
        @Scheduled(cron = "*/10 * * * * ?")  //采用间隔调度,每10秒执行一次
        public void runJobb(){ //定义一个执行的任务
            log.info("[*******MyTaskB -- 间隔调度 ******]"+
                    new SimpleDateFormat("yyy-MM-dd HH:mm:ss.SSS").format(new Date()));
        }
    
    }
    

    此类中包含两种定时方式的定时任务(1.每n秒执行一次定时任务,2.按照cron表达式执行任务),使用方法只需要在执行业务的方法前加@Scheduled注解即可,根据不同场景,使用适当的定时方式执行定时任务

    2.2.1 在与DemoApplication同级的包下新建一个Schedule的配置类SchedulerAutoConfiguration,用于通过ScheduleFactory生成schedule实例:


    SchedulerAutoConfiguration.png
    package com.example.demo;
    import org.quartz.Scheduler;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.quartz.SchedulerFactoryBean;
    
    import java.io.IOException;
    
    /**
     * 类功能描述:<br>
     * <ul>
     * <li>类功能描述1<br>
     * <li>类功能描述2<br>
     * <li>类功能描述3<br>
     * </ul>
     * 修改记录:<br>
     * <ul>
     * <li>修改记录描述1<br>
     * <li>修改记录描述2<br>
     * <li>修改记录描述3<br>
     * </ul>
     *
     * @author xuefl
     * @version 5.0 since 2019-12-19
     */
    @Configuration
    public class SchedulerAutoConfiguration {
    
        //这个地方如果需要使用自定义的executor,可以在别的地方配置好,然后这里注入
        //@Autowired
        //private ThreadPoolTaskExecutor taskExecutor;
    
    
        @Bean(name="SchedulerFactory")
        public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
            SchedulerFactoryBean factory = new SchedulerFactoryBean();
            factory.setAutoStartup(true);
            //这里如果不配置任务池,它就会默认加载SimpleThreadPool
            //factory.setTaskExecutor();
            return factory;
        }
    
        @Bean(name="funnyScheduler")
        public Scheduler scheduler() throws IOException {
            return schedulerFactoryBean().getScheduler();
        }
    }
    

    此处定义的Bean funnyScheduler会被后续schedule对象操作类中注入,注入时,名称必须一致
    2.2.2 在service包下新建JobScheduleService接口,定义对调度任务的操作抽象方法


    JobScheduleService.png
    package com.example.demo.service;
    import java.util.Date;
    
    /**
     * 类功能描述:<br>
     * <ul>
     * <li>类功能描述1<br>
     * <li>类功能描述2<br>
     * <li>类功能描述3<br>
     * </ul>
     * 修改记录:<br>
     * <ul>
     * <li>修改记录描述1<br>
     * <li>修改记录描述2<br>
     * <li>修改记录描述3<br>
     * </ul>
     *
     * @author xuefl
     * @version 5.0 since 2019-12-19
     */
    public interface JobScheduleService {
    
        /**
         * 功能描述: 添加简单任务
         * 可以自定义一个任务信息对象,然后从信息对象中获取参数创建简单任务
         *
         * @param
         * @return:void
         * @since: v1.0
         * @Author:xf
         * @Date: 2019/3/15 17:00
         */
        void addSimpleJob(Class taskClass, String jobName, String jobGroup, Date startTime, Date endTime);
    
        /**
         * 功能描述: 添加定时任务
         * 可以自定义一个任务信息对象,然后从信息对象中获取参数创建定时任务
         *
         * @param
         * @return:void
         * @since: v1.0
         * @Author:xf
         * @Date: 2019/3/15 17:00
         */
        void addCronJob(Class taskClass, String jobName, String jobGroup, Date startTime, Date endTime, String cronExpression);
    
        /**
         * 功能描述: 修改任务Trigger,即修改任务的定时机制
         *
         * @param
         * @return:void
         * @since: v1.0
         * @Author:xf
         * @Date: 2019/3/15 17:00
         */
        void modifyJob(String jobName, String jobGroup, String cronExpression);
    
        /**
         * 功能描述: 暂停任务,只支持定时任务的暂停,不支持单次任务,单次任务需要interrupt
         *
         * @param
         * @return:void
         * @since: v1.0
         * @Author:xf
         * @Date: 2019/3/15 17:00
         */
        void pauseJob(String jobName, String jobGroup);
    
        /**
         * 功能描述: 从暂停状态中恢复定时任务运行
         *
         * @param
         * @return:void
         * @since: v1.0
         * @Author:xf
         * @Date: 2019/3/15 17:00
         */
        void resumeJob(String jobName, String jobGroup);
    
        /**
         * 功能描述: 删除任务
         *
         * @param
         * @return:void
         * @since: v1.0
         * @Author:xf
         * @Date: 2019/3/15 17:00
         */
        void deleteJob(String jobName, String jobGroup);
    
    }
    

    2.2.3 在service.impl包下新建JobScheduleService接口的实现类JobScheduleServiceImpl


    JobScheduleServiceImpl.png
    package com.example.demo.service.impl;
    import com.example.demo.service.JobScheduleService;
    import lombok.extern.slf4j.Slf4j;
    import org.quartz.*;
    import org.quartz.impl.triggers.CronTriggerImpl;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    import org.springframework.util.StringUtils;
    
    import java.text.ParseException;
    import java.util.Date;
    
    /**
     * 类功能描述:<br>
     * <ul>
     * <li>类功能描述1<br>
     * <li>类功能描述2<br>
     * <li>类功能描述3<br>
     * </ul>
     * 修改记录:<br>
     * <ul>
     * <li>修改记录描述1<br>
     * <li>修改记录描述2<br>
     * <li>修改记录描述3<br>
     * </ul>
     *
     * @author xuefl
     * @version 5.0 since 2019-12-19
     */
    @Service
    @Slf4j
    public class JobScheduleServiceImpl implements JobScheduleService {
    
        /**
         * 因为在配置中设定了这个bean的名称,这里就需要指定bean的名称,不然启动就会报错
         */
        @Autowired
        @Qualifier("funnyScheduler")
        private Scheduler scheduler;
    
        /**
         * 功能描述: 添加简单任务
         *
         * @param
         * @return:void
         * @since: v1.0
         * @Author:xf
         * @Date: 2019/3/15 17:00
         */
        @Override
        public void addSimpleJob(Class taskClass, String jobName, String jobGroup, Date startTime, Date endTime) {
            JobDetail jobDetail = JobBuilder.newJob(taskClass).withIdentity(jobName, jobGroup).build();
            SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger()
                    .withIdentity(jobName, jobGroup)
                    .startAt(startTime)
                    .endAt(endTime)
                    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                            .withIntervalInSeconds(3)
                            .withRepeatCount(5))
                    .build();
            try {
                scheduler.scheduleJob(jobDetail, simpleTrigger);
            } catch (SchedulerException e) {
                log.error("addSimpleJob catch {}", e.getMessage());
            }
        }
    
        /**
         * 功能描述: 添加定时任务
         *
         * @param
         * @return:void
         * @since: v1.0
         * @Author:xf
         * @Date: 2019/3/15 17:00
         */
        @Override
        public void addCronJob(Class taskClass, String jobName, String jobGroup, Date startTime, Date endTime, String cronExpression) {
            JobDetail jobDetail = JobBuilder.newJob(taskClass).withIdentity(jobName, jobGroup).build();
            // 触发器
    
            try {
                CronTrigger trigger = new CronTriggerImpl(jobName, jobGroup, jobName, jobGroup, startTime, endTime, cronExpression);// 触发器名,触发器组
                scheduler.scheduleJob(jobDetail, trigger);
            } catch (SchedulerException | ParseException e) {
                log.error("addCronJob catch {}", e.getMessage());
            }
        }
    
        /**
         * 功能描述: 修改任务Trigger,即修改任务的定时机制
         *
         * @param
         * @return:void
         * @since: v1.0
         * @Author:xf
         * @Date: 2019/3/15 17:00
         */
        @Override
        public void modifyJob(String jobName, String jobGroup, String cronExpression) {
            TriggerKey oldKey = new TriggerKey(jobName, jobGroup);
            //表达式调度构建器(即任务执行的时间)
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            //按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().
                    withIdentity(jobName, jobGroup).withSchedule(scheduleBuilder).build();
            try {
                scheduler.rescheduleJob(oldKey, trigger);
            } catch (SchedulerException e) {
                log.error("modifyJob catch {}", e.getMessage());
            }
        }
    
        /**
         * 功能描述: 暂停任务,只支持定时任务的暂停,不支持单次任务,单次任务需要interrupt
         *
         * @param
         * @return:void
         * @since: v1.0
         * @Author:xf
         * @Date: 2019/3/15 17:00
         */
        @Override
        public void pauseJob(String jobName, String jobGroup) {
            JobKey jobKey = new JobKey(jobName, jobGroup);
            try {
                JobDetail jobDetail = scheduler.getJobDetail(jobKey);
                if (StringUtils.isEmpty(jobDetail)) {
                    System.out.println("没有这个job");
                }
                scheduler.pauseJob(jobKey);
            } catch (SchedulerException e) {
                log.error("pauseJob catch {}", e.getMessage());
            }
        }
    
        /**
         * 功能描述: 从暂停状态中恢复定时任务运行
         *
         * @param
         * @return:void
         * @since: v1.0
         * @Author:xf
         * @Date: 2019/3/15 17:00
         */
        @Override
        public void resumeJob(String jobName, String jobGroup) {
            JobKey jobKey = new JobKey(jobName, jobGroup);
            try {
                scheduler.resumeJob(jobKey);
            } catch (SchedulerException e) {
                log.error("resumeJob catch {}", e.getMessage());
            }
        }
    
        /**
         * 功能描述: 删除任务
         *
         * @param
         * @return:void
         * @since: v1.0
         * @Author:xf
         * @Date: 2019/3/15 17:00
         */
        @Override
        public void deleteJob(String jobName, String jobGroup) {
            JobKey jobKey = new JobKey(jobName, jobGroup);
            try {
                scheduler.deleteJob(jobKey);
            } catch (SchedulerException e) {
                log.error("deleteJob catch {}", e.getMessage());
            }
        }
    }
    

    此类实现了JobScheduleService 接口定义的对调度任务各种操作的具体实现步骤,通过注入funnyScheduler来操作。
    2.2.4 定义了对调度任务的操作类后,需要增加自己的调度任务业务实现类,也就是任务具体要干的事,需要实现quartz中的Job接口,并重写其execute方法,在其中增加自己的业务流程,在schedule包中新建job包,并在job包下新建SimpleJob类:


    SimpleJob.png
    package com.example.demo.schedule.job;
    import lombok.extern.slf4j.Slf4j;
    import org.quartz.Job;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    
    /**
     * 类功能描述:<br>
     * <ul>
     * <li>类功能描述1<br>
     * <li>类功能描述2<br>
     * <li>类功能描述3<br>
     * </ul>
     * 修改记录:<br>
     * <ul>
     * <li>修改记录描述1<br>
     * <li>修改记录描述2<br>
     * <li>修改记录描述3<br>
     * </ul>
     *
     * @author xuefl
     * @version 5.0 since 2019-12-19
     */
    @Slf4j
    public class SimpleJob implements Job {
        @Override
        public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            log.info("简单任务执行中");
        }
    }
    

    2.2.5 最后这个任务是怎么被调用的呢?其实在任何地方,业务执行过程中,rest接口中,都可以对这个任务进行CUD操作:


    scheduleSimpleJob.png
    package com.example.demo.controller;
    import com.example.demo.schedule.job.SimpleJob;
    import com.example.demo.service.JobScheduleService;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.http.MediaType;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    import java.util.Date;
    
    /**
     * 类功能描述:<br>
     * <ul>
     * <li>类功能描述1<br>
     * <li>类功能描述2<br>
     * <li>类功能描述3<br>
     * </ul>
     * 修改记录:<br>
     * <ul>
     * <li>修改记录描述1<br>
     * <li>修改记录描述2<br>
     * <li>修改记录描述3<br>
     * </ul>
     *
     * @author xuefl
     * @version 5.0 since 2019-12-18
     */
    @RestController
    @Api(value = "SwaggerValue", tags={"SwaggerController"},description = "swagger应用",  produces = MediaType.APPLICATION_JSON_VALUE)
    public class SimpleController {
        @Resource
        private JobScheduleService jobScheduleService;
    
        @RequestMapping(value = "/hello", method = RequestMethod.GET)
        @ApiOperation(value="hello",httpMethod = "GET",notes="hello",produces = MediaType.APPLICATION_JSON_VALUE)
        public String sayHello() {
            return "hello world";
        }
    
        @RequestMapping(value = "/scheduleSimpleJob", method = RequestMethod.POST)
        @ApiOperation(value="scheduleSimpleJob",httpMethod = "POST",notes="scheduleSimpleJob",produces = MediaType.APPLICATION_JSON_VALUE)
        public void scheduleSimpleJob() {
            jobScheduleService.addSimpleJob(SimpleJob.class,
                    "simpleJob", "simpleJob", new Date(), null);
        }
    
    }
    

    注入jobScheduleService对象后 增加一个接口/scheduleSimpleJob,增加其实现业务方法,通过jobScheduleService对象对调度任务进行管理,此处只以addSimpleJob为例,其他方法大家可以增加rest接口以测试

    1. 启动后运行日志如下


      调度任务线程池实例化.png
      第一种定时任务运行日志.png
      第二种调度任务,接口调用后运行日志.png

    相关文章

      网友评论

        本文标题:springboot 之集成quartz

        本文链接:https://www.haomeiwen.com/subject/gtmznctx.html