springboot集成quartz实时性的任务调度

quartz简介(官网搬下来的!!!):

Quartz是功能强大的开源作业调度库,几乎可以集成到任何Java应用程序中-从最小的独立应用程序到最大的电子商务系统。Quartz可用于创建简单或复杂的计划,以执行数以万计,数以万计的工作。任务定义为标准Java组件的作业,它们实际上可以执行您可以对其执行的任何编程操作。Quartz Scheduler包含许多企业级功能,例如对JTA事务和集群的支持。Quartz是免费使用的,并根据Apache 2.0许可获得许可

quartz官网:http://www.quartz-scheduler.org/

Quartz可以用来解决定时任务调度的问题,那么他和Spring Task以及ScheduledThreadPool有哪些区别,Quartz的优势在哪里?

Spring Task:

1、默认单线程执行

2、同一个任务执行完之后才可以执行下一个该任务(可配置线程池解决)

3、修改定时计划需要重启项目使之生效

4、任务抛出异常会导致后面的任务不继续执行

5、同一任务多次执行使用相同的任务实例

Quartz:

1、默认多线程执行(默认10个线程,可配置)

2、多任务执行之间互不影响,快慢由cpu决定

3、可以不重启服务的情况动态的修改定时任务频率,实时生效

4、某次任务抛出异常不会影响下次任务的执行

5、每次任务执行都会创建一个新的任务实例执行任务

ScheduledThreadPool:

1、可以实现多线程执行任务

2、没有全面的任务调度api,需要开发者结合线程池的方法自行开发定时功能

解决问题:

1、动态的定时任务(修改定时计划后不需要重启项目实时生效)

2、任务抛出异常后不能影响下次任务的正常执行

效果图:

话不多说,直接放码:

数据库表:

CREATE TABLE `sys_job` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `job_group` varchar(256) DEFAULT NULL COMMENT '任务组',
  `job_name` varchar(256) DEFAULT NULL COMMENT '任务名称',
  `cron_expression` varchar(128) DEFAULT NULL COMMENT 'cron表达式',
  `bean_class` varchar(512) DEFAULT NULL COMMENT '任务类的全路径名',
  `job_status` varchar(64) DEFAULT NULL COMMENT '任务状态',
  `description` varchar(255) DEFAULT NULL COMMENT '描述',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `modify_time` datetime DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `jobGroup_jobName_unique` (`job_group`,`job_name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

INSERT INTO `sys_job`(`id`, `job_group`, `job_name`, `cron_expression`, `bean_class`, `job_status`, `description`, `create_time`, `modify_time`) VALUES (1, 'test', 'test_one', '*/10 * * * * ?', 'com.lll.quartz.quartz.job.TestOneJob', 'normal', '第一个测试任务', '2020-12-11 00:00:00', '2020-12-11 00:00:00');
INSERT INTO `sys_job`(`id`, `job_group`, `job_name`, `cron_expression`, `bean_class`, `job_status`, `description`, `create_time`, `modify_time`) VALUES (2, 'test', 'test_two', '30 * * * * ?', 'com.lll.quartz.quartz.job.TestTwoJob', 'normal', '第二个测试任务', '2020-12-11 00:00:00', '2020-12-11 00:00:00');

首先说明,笔者的boot项目版本为spring-boot-2-1-12-released

添加maven依赖,quartz的依赖版本为2.3.2,是目前(2020-12-11)最新版本

<!-- quartz -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

添加相应的配置类

@Configuration
public class QuartzConfig {

    @Autowired
    private JobFactory jobFactory;

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        try {
            //设置覆盖已存在的任务
            schedulerFactoryBean.setOverwriteExistingJobs(true);
            //延迟1秒执行
            schedulerFactoryBean.setStartupDelay(1);
            //将spring管理job自定义工厂交由调度器维护
            schedulerFactoryBean.setJobFactory(jobFactory);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return schedulerFactoryBean;
    }


    @Bean(name = "scheduler")
    public Scheduler scheduler() {
        return schedulerFactoryBean().getScheduler();
    }


}

工厂类:

@Component
public class JobFactory extends AdaptableJobFactory {
    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //调用父类的方法
        Object jobInstance = super.createJobInstance(bundle);
        //进行注入
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

基础任务类:

public interface BaseJob extends Job {
    @Override
    void execute(JobExecutionContext context) throws JobExecutionException;
}

任务1:

@Slf4j
public class TestOneJob implements BaseJob {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        log.info("嘤嘤嘤,人家是第一个定时任务!,现在的时间是:{}", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
    }
}

任务2:

@Slf4j
public class TestTwoJob implements BaseJob {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        log.info("哇哈哈,我是第二个定时任务!,现在的时间是:{}", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
    }
}

初始化监听类(作用是项目启动时将之前持久化的的任务初始化)

@Component
public class ScheduleJobInitListener implements CommandLineRunner {

    @Autowired
    private ISysJobService sysJobService;

    @Override
    public void run(String... args) throws Exception {
        sysJobService.init();
    }
}

service代码,处理任务初始化,添加,删除,修改,暂停,恢复

@Slf4j
@Service
@Transactional
public class SysJobServiceImpl implements ISysJobService {

    @Autowired
    private Scheduler scheduler;

    @Resource
    private SysJobMapper sysJobMapper;

    @PostConstruct
    public void startScheduler() {
        try {
            // 启动调度器
            scheduler.start();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void init() {
        List<SysJob> sysJobs = sysJobMapper.selectList();
        try {
            for(SysJob sysJob : sysJobs){
                // 构建Job信息
                Class<?> clazz = Class.forName(sysJob.getBeanClass());
                BaseJob baseJob = (BaseJob) clazz.newInstance();
                JobDetail jobDetail = JobBuilder.newJob(baseJob.getClass()).withIdentity(sysJob.getBeanClass(), sysJob.getJobGroup()).build();

                // Cron表达式调度构建器(即任务执行的时间)
                /**
                 * withMisfireHandlingInstructionDoNothing
                 * ——不触发立即执行
                 * ——等待下次Cron触发频率到达时刻开始按照Cron频率依次执行
                 */
                CronScheduleBuilder cron = CronScheduleBuilder.cronSchedule(sysJob.getCronExpression()).withMisfireHandlingInstructionDoNothing();

                //根据Cron表达式构建一个Trigger
                CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(sysJob.getBeanClass(), sysJob.getJobGroup()).withSchedule(cron).build();

                scheduler.scheduleJob(jobDetail, trigger);
                if(JobStatusEnum.stop.getStatus().equals(sysJob.getJobStatus())){
                    scheduler.pauseJob(JobKey.jobKey(sysJob.getBeanClass(), sysJob.getJobGroup()));
                }

            }
        } catch (Exception e) {
            log.error("【定时任务】创建失败!", e);
            throw new RuntimeException("【定时任务】创建失败!");
        }
    }

    @Override
    public List<SysJob> list(Paginator paginator) {
        PageHelper.startPage(paginator.getPageNum(), paginator.getPageSize());
        List<SysJob> sysJobs = sysJobMapper.selectList();
        paginator.setTotal(new PageInfo(sysJobs).getTotal());
        return sysJobs;
    }

    @Override
    public void add(SysJob sysJob) {
        // 持久化数据
        sysJob.setJobStatus(JobStatusEnum.normal.getStatus());
        sysJob.setCreateTime(new Date());
        sysJobMapper.insert(sysJob);

        try {
            // 构建Job信息
            Class<?> clazz = Class.forName(sysJob.getBeanClass());
            BaseJob baseJob = (BaseJob) clazz.newInstance();
            JobDetail jobDetail = JobBuilder.newJob(baseJob.getClass()).withIdentity(sysJob.getBeanClass(), sysJob.getJobGroup()).build();

            // Cron表达式调度构建器(即任务执行的时间)
            CronScheduleBuilder cron = CronScheduleBuilder.cronSchedule(sysJob.getCronExpression()).withMisfireHandlingInstructionDoNothing();

            //根据Cron表达式构建一个Trigger
            CronTrigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(sysJob.getBeanClass(), sysJob.getJobGroup()).withSchedule(cron).build();

            scheduler.scheduleJob(jobDetail, trigger);
        } catch (Exception e) {
            log.error("【定时任务】创建失败!", e);
            throw new RuntimeException("【定时任务】创建失败!");
        }

    }

    @Override
    public void update(SysJob sysJob) {
        // 持久化数据
        SysJob dbSysJob = sysJobMapper.selectByPrimaryKey(sysJob.getId());
        if(dbSysJob == null){
            return;
        }
        sysJob.setModifyTime(new Date());
        sysJobMapper.updateByPrimaryKeySelective(sysJob);

        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(dbSysJob.getBeanClass(), dbSysJob.getJobGroup());
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(sysJob.getCronExpression()).withMisfireHandlingInstructionDoNothing();

            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

            // 根据Cron表达式构建一个Trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
            // 如果任务之前是暂停状态,rescheduleJob方法会重启该任务,需要手动去pauseJob
            if(JobStatusEnum.stop.getStatus().equals(dbSysJob.getJobStatus())){
                scheduler.pauseJob(JobKey.jobKey(dbSysJob.getBeanClass(), dbSysJob.getJobGroup()));
            }
        } catch (Exception e) {
            log.error("【定时任务】更新失败!", e);
            throw new RuntimeException("【定时任务】创建失败!");
        }
    }

    @Override
    public void pause(Integer id) {
        SysJob dbSysJob = sysJobMapper.selectByPrimaryKey(id);
        if(dbSysJob == null){
            return;
        }
        SysJob sysJob = new SysJob();
        sysJob.setId(id);
        sysJob.setJobStatus(JobStatusEnum.stop.getStatus());
        sysJob.setModifyTime(new Date());
        sysJobMapper.updateByPrimaryKeySelective(sysJob);

        try {
            scheduler.pauseJob(JobKey.jobKey(dbSysJob.getBeanClass(), dbSysJob.getJobGroup()));
        } catch (Exception e) {
            log.error("【定时任务】暂停失败!", e);
            throw new RuntimeException("【定时任务】暂停失败!");
        }
    }

    @Override
    public void resume(Integer id) {
        SysJob dbSysJob = sysJobMapper.selectByPrimaryKey(id);
        if(dbSysJob == null){
            return;
        }
        SysJob sysJob = new SysJob();
        sysJob.setId(id);
        sysJob.setJobStatus(JobStatusEnum.normal.getStatus());
        sysJob.setModifyTime(new Date());
        sysJobMapper.updateByPrimaryKeySelective(sysJob);

        try {
            scheduler.resumeJob(JobKey.jobKey(dbSysJob.getBeanClass(), dbSysJob.getJobGroup()));
        } catch (Exception e) {
            log.error("【定时任务】恢复失败!", e);
            throw new RuntimeException("【定时任务】恢复失败!");
        }
    }

    @Override
    public void delete(Integer id) {
        SysJob dbSysJob = sysJobMapper.selectByPrimaryKey(id);
        if(dbSysJob == null){
            return;
        }
        sysJobMapper.deleteByPrimaryKey(id);

        try {
            scheduler.pauseTrigger(TriggerKey.triggerKey(dbSysJob.getBeanClass(), dbSysJob.getJobGroup()));
            scheduler.unscheduleJob(TriggerKey.triggerKey(dbSysJob.getBeanClass(), dbSysJob.getJobGroup()));
            scheduler.deleteJob(JobKey.jobKey(dbSysJob.getBeanClass(), dbSysJob.getJobGroup()));
        } catch (Exception e) {
            log.error("【定时任务】删除失败!", e);
            throw new RuntimeException("【定时任务】删除失败!");
        }
    }

还有一些dao层的及前端代码就不贴了,全部代码详见:https://github.com/liangliang1006/lll-quartz.git

获取代码后直接修改一下数据库连接,直接启动就好叻

扩展:

其实我这个demo只是展示了quartz的部分功能,他的还可以实现任务流调度,即a任务完成后触发b任务,同时可以支持任务执行到一定时间或次数后停止继续执行;

因为我也是接触此框架不久,有兴趣的可以一起留言讨论

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值