微服务springboot

springboot 之集成quartz

2019-12-23  本文已影响0人  xuefly3

前言

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

环境准备

开始集成

  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
上一篇下一篇

猜你喜欢

热点阅读