文档章节

Spring Boot集成Quartz-动态任务管理

wuxinshui
 wuxinshui
发布于 2017/09/02 13:38
字数 1572
阅读 45
收藏 1
点赞 0
评论 0

Quartz提供了一组丰富的API,来管理Job。

前言

当定时任务越来越多时,集中管理Job越有必要。Quartz提供了一组丰富的API,来管理Job。

Spring Boot 定时任务之Quartz中讲了Spring Boot怎么集成quartz,这里结合实际业务,参考网上一些经验,总结一下集成的一些坑。

动态任务管理

下面以官方的例子来说明一下这些常用的API。

定义Job实现类

public class ColorJob implements Job {

    public ColorJob() {
        // Instances of Job must have a public no-argument constructor.
    }

    public void execute(JobExecutionContext context)
            throws JobExecutionException {

        JobDataMap data = context.getMergedJobDataMap();
        System.out.println("someProp = " + data.getString("someProp"));
    }

}

定义JobDetailTrigger

// Define job instance
JobDetail job1 = newJob(ColorJob.class)
    .withIdentity("job1", "group1")
    .build();

// Define a Trigger that will fire "now", and not repeat
Trigger trigger1 = newTrigger()
    .withIdentity("trigger1", "group1")
    .startNow()
    .build();

新建

把Job添加到调度器sched中。

// Add the the job to the scheduler's store
sched.add(job1, trigger1);

更新

TriggerKey triggerKey = TriggerKey.triggerKey("job1", "group1");
// store, and set overwrite flag to 'true'  
scheduler.addJob(job1, true);
// tell the scheduler to remove the old trigger with the given key, and put the new one in its place
scheduler.rescheduleJob(triggerKey, trigger1);

删除

JobKey jobKey = JobKey.jobKey("job1", "group1");
scheduler.deleteJob(jobKey);

暂停

JobKey jobKey = JobKey.jobKey("job1", "group1");
scheduler.pauseJob(jobKey);

恢复

JobKey jobKey = JobKey.jobKey("job1", "group1");
scheduler.resumeJob(jobKey);

立即执行

JobKey jobKey = JobKey.jobKey("job1", "group1");
scheduler.triggerJob(jobKey);

对Job的动态管理实际就是对调度器schedule中的JobDetail做动态操作。增删改逻辑不想其他业务逻辑一样,直接操作数据库。它是先操作schedule中的JobDetail,然后根据JobKey或者TriggerKey同步更新数据库的Job。

实际开发中常用的也就是这几种操作,quartz还有其他多种操作。如暂停所有的Job、打断正在执行的Job等。感兴趣的同学可以参考官网API:org.quartz.core.QuartzScheduler

加载Job

要实现Job的动态管理,Job必须要持久化到数据库。Spring 容器在启动的时候,从数据库加载所有的Job。

实现接口CommandLineRunner。定义初始化操作。

   @Override
    public void run(String... strings) throws Exception {
        //查询数据库中的Job
        List<JobInfo> jobInfoList = jobInfoMapper.selectAll();
        List<JobInfoVo> jobInfoVoList = BeanUtils.copyList(jobInfoList, JobInfoVo.class);

        //根据数据库中Job的状态同步scheduler中的状态。
        for (int i = 0; i < jobInfoVoList.size(); i++) {
            JobInfoVo jobVo = jobInfoVoList.get(i);
            Class jobClass = Class.forName(jobVo.getJobClass());

            JobKey jobKey = JobKey.jobKey(jobVo.getJobName(), jobVo.getJobGroup());
            TriggerKey triggerKey = TriggerKey.triggerKey(jobVo.getTriggerName(), jobVo.getTriggerGroup());
            JobDetail job1 = newJob(jobClass)
                    .withIdentity(jobKey)
                    .storeDurably()
                    .build();
            Trigger trigger = newTrigger().withSchedule(CronScheduleBuilder.cronSchedule(jobVo.getCronExpression()))
                    .withIdentity(triggerKey)
                    .build();

            JobStatus jobStatus = JobStatus.valueOf(jobVo.getJobStatus());
            switch (jobStatus) {
                case RUNNING:
                    scheduler.scheduleJob(job1, trigger);
                    break;
                case PAUSE:
                    scheduler.scheduleJob(job1, trigger);
                    scheduler.pauseJob(jobKey);
            }
        }
        //添加Job监听器
        QuartzJobListener quartzJobListener = new QuartzJobListener("quartzListener",jobManagerService);
        scheduler.getListenerManager().addJobListener(quartzJobListener, allJobs());
    }

下面总结一下,根据项目需要,遇到的一些问题。。

坑一:初始化调度器

Spring提供了xml、注解、Java配置、groovy配置实现Bean的创建和注入。通过xml来配置quartz是非常方便的,但是在Spring Boot中不推荐xml文件配置,采用Java配置来实现。

1.使用初始化Job

一开始初始化调度器是参考官网34.6 Using the Quartz Scheduler来实现Java配置的。

这里写图片描述

使用这种方式有个问题:必须创建一个空的Job实现类,来实现Schedule的初始化。

2.SchedulerFactory获取Schedule

@Configuration
public class QuartzConfig {

    @Bean(name = "scheduler")
    public Scheduler scheduler(QuartzJobFactory quartzJobFactory) throws Exception {

        SchedulerFactoryBean factoryBean=new SchedulerFactoryBean();
        factoryBean.setJobFactory(quartzJobFactory);
        factoryBean.afterPropertiesSet();
        Scheduler scheduler=factoryBean.getScheduler();
        scheduler.start();
        return scheduler;
    }
}

从1->2,是问题的一个解决优化的过程。

坑二:Spring Bean不能注入Job的实现类中

QuartzConfig中添加以下bean

    //解决Job中注入Spring Bean为null的问题
    @Component("quartzJobFactory")
    private class QuartzJobFactory extends AdaptableJobFactory {
        //这个对象Spring会帮我们自动注入进来,也属于Spring技术范畴.
        @Autowired
        private AutowireCapableBeanFactory capableBeanFactory;

        protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
            //调用父类的方法
            Object jobInstance = super.createJobInstance(bundle);
            //进行注入,这属于Spring的技术,不清楚的可以查看Spring的API.
            capableBeanFactory.autowireBean(jobInstance);
            return jobInstance;
        }
    }

Job的实现类中添加以下代码:

    protected void springBeanAutowiringSupport() {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
    }

坑三:保存Job执行记录

保存执行记录也有2种设计方案。

方案1:每个Job执行时保存

Job在执行时,调用业务bean中的方法,保存执行记录。

这里写图片描述

方案2:添加监听器JobListenerSupport

Quartz提供的监听器有JobListenerTriggerListener,这里我们使用JobListener,该监听器接口有三个方法:

这里写图片描述

实现接口JobListener,定义监听业务。

这里写图片描述

添加监听器到调度器schedule中。

scheduler.getListenerManager().addJobListener(quartzJobListener, allJobs());

为了减少对Job的侵入性,建议在监听器中做业务逻辑的相关操作。Job只负责执行即可,不负责业务逻辑处理。

坑四:Job监听器中注入业务Bean

坑二是在Job中注入业务bean,发现在JobListenerSupport中注入Bean也是为null。之前的填坑方法不管用了。。。

可以通过构造方法来注入。

这里写图片描述

QuartzJobListener quartzJobListener = new QuartzJobListener("quartzListener",jobManagerService);
scheduler.getListenerManager().addJobListener(quartzJobListener, allJobs());

坑五:Job并发

quartz中的Job并发有2种情况
1. 间隔时间小于执行时间,上一个任务还未完成,开始执行新的任务;
2. 同一个Job,有相同的触发时间。

第二种情况设计上可以避免,cron定时表达式,能满足大部分情况下的使用,完全没有必要创建2个触发时间相同的Job。

第一种情况下的并发,可以使用注解@DisallowConcurrentExecution

这里写图片描述

加上注解以后,如果第一次执行未完成,第二次会等待直到第一次完成。

参考

Spring Boot配置方式
34.6 Using the Quartz Scheduler

参考的东西太多了,都是一些优秀的博客。。。

1.校验定时表达式

http://blog.csdn.net/lisheng19870305/article/details/39553925
http://blog.csdn.net/ukulelepku/article/details/54310035
https://stackoverflow.com/questions/2362985/verifying-a-cron-expression-is-valid-in-java
https://gist.github.com/andrew-templeton/ae4126a8efe219b796a3
https://codereview.stackexchange.com/questions/63363/cron-expression-validator-for-apache-quartz

2.Job中注入Spring Bean为Null

http://blog.csdn.net/jackylovesjava/article/details/50044271
http://blog.csdn.net/colorandsong/article/details/40392117
http://bbs.csdn.net/topics/392182530
http://www.cnblogs.com/feiqihang/p/5358100.html
http://blog.csdn.net/colorandsong/article/details/40392117
http://www.cnblogs.com/feiqihang/p/5358100.html
http://blog.csdn.net/educast/article/details/73331398
http://blog.csdn.net/gui66497/article/details/53589476
https://zhidao.baidu.com/question/984255639590451619.html
http://www.cnblogs.com/daxin/p/3608320.html
https://www.zhihu.com/question/26850120

3.Quartz日志处理

https://stackoverflow.com/questions/22573824/quartz-schedular-triggers-histoy-need-save-in-data-base-after-firing
https://stackoverflow.com/questions/22479622/how-to-maintain-the-quartz-triggers-history-in-data-base-after-firing-in-java
https://www.quartz-scheduler.net/documentation/faq.html
https://github.com/Flipkart/quartz/blob/master/quartz-plugins/src/main/java/org/quartz/plugins/history/LoggingTriggerHistoryPlugin.java

4.quartz动态任务管理

http://blog.csdn.net/u012907049/article/details/73801122
https://help.aliyun.com/document_detail/43140.html?spm=5176.doc43132.6.696.srgKu4

5.Quartz官网实践

http://www.quartz-scheduler.org/documentation/best-practices.html#listeners-(triggerlistener-joblistener-schedulerlistener


http://www.quartz-scheduler.org/documentation/quartz-2.2.x/cookbook/JobListeners.html

6.quartz任务并发

http://blog.csdn.net/tiantangpw/article/details/41120137
https://segmentfault.com/a/1190000009128328
https://my.oschina.net/blueskyer/blog/325812

7.暂停是否影响当前正在运行的Job。删除?

https://stackoverflow.com/questions/27823159/difference-between-pausejob-and-pausetrigger-in-quartz-scheduler
http://forums.terracotta.org/forums/posts/list/4537.page
https://stackoverflow.com/questions/16173785/quartz-fails-to-delete-a-job-now-what

© 著作权归作者所有

共有 人打赏支持
wuxinshui
粉丝 2
博文 54
码字总数 28673
作品 0
普陀
程序员
Spring Boot--自定义Starter之spring-boot-starter-quartz

痛点 在Spring中使用计划任务,有两种方案可供选择:一、用Spring原生的计划任务,使用起来非常简单,只需要用到@Scheduled注解即可;二、集成Quartz来做计划任务,需要配置大量的Quartz原生...

大大枣 ⋅ 04/03 ⋅ 0

Spring Boot集成Quartz注入Spring管理的类

Spring有自己的Schedule定时任务,在Spring boot中使用的时候,不能动态管理JOB,于是就使用Quartz来实现。 在Spring Boot中配置Quartz: 为了在JOB中使用Spring管理的Bean,需要重新定义一个...

风中的眼睛 ⋅ 2016/06/21 ⋅ 1

liuht777/uncode-scheduler

uncode-schedule 基于Spring Task + Zookeeper的分布式任务调度组件,非常小巧,使用简单,只需要引入jar包。不需要单独部署服务端。确保所有任务在集群中不重复,不遗漏的执行。支持动态添加...

liuht777 ⋅ 2017/10/26 ⋅ 0

Java Web定时任务这一篇就够了

一、Java定时任务 1、Timer 包下面一个工具类,从1.3开始便支持了; 说明下后两个参数分别是delay延迟执行,和period执行间隔,单位都是毫秒。 2、ScheduledExecutorService 包下面,从1.5开...

叫我宫城大人 ⋅ 04/16 ⋅ 0

分布式定时任务Elastic-Job框架在SpringBoot工程中的应用实践(一)

摘要:如何构建具备作业分片和弹性扩缩容的定时任务系统是每个大型业务系统在设计时需要考虑的重要问题? 对于构建一般的业务系统来说,使用Quartz或者Spring Task即可基本满足我们的单体服用...

癫狂侠 ⋅ 05/12 ⋅ 0

spring boot 集成 quartz,实现动态控制

spring boot 集成 quartz spring boot 自带的定时任务 schedule使用起来非常简单,但是不灵活。如果需要动态的去修改任务间隔,可以实现,但就是比较麻烦。这里记录一下quartz在spring boot...

alantuling_jt ⋅ 2017/10/26 ⋅ 0

SpringMVC + MyBatis整合 【转】

环境:spring3.1.1+mybatis3.2.8+mybatis-spring1.2.3 网络上关于这个架构的搭建文章,实在是太多了,本文是对于本人初次搭建时的一些注意点的整理。 主要是一些配置文件的内容和架构的目录。...

如何让他和 ⋅ 2016/09/30 ⋅ 0

SpringMVC + MyBatis整合

环境:spring3.1.1+mybatis3.2.8+mybatis-spring1.2.3 网络上关于这个架构的搭建文章,实在是太多了,本文是对于本人初次搭建时的一些注意点的整理。 主要是一些配置文件的内容和架构的目录。...

如何让他和 ⋅ 2016/08/19 ⋅ 0

SpringMVC + MyBatis整合 【转】

环境:spring3.1.1+mybatis3.2.8+mybatis-spring1.2.3 网络上关于这个架构的搭建文章,实在是太多了,本文是对于本人初次搭建时的一些注意点的整理。 主要是一些配置文件的内容和架构的目录。...

梵蒂冈考虑过 ⋅ 2016/09/18 ⋅ 1

SpringMVC + MyBatis整合 【转】

环境:spring3.1.1+mybatis3.2.8+mybatis-spring1.2.3 网络上关于这个架构的搭建文章,实在是太多了,本文是对于本人初次搭建时的一些注意点的整理。 主要是一些配置文件的内容和架构的目录。...

梵蒂冈考虑过 ⋅ 2016/09/08 ⋅ 1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

骰子游戏代码开源地址

因为阿里云现在服务器已经停用了,所以上面的配置已经失效。 服务端开源地址:https://gitee.com/goalya/chat4.git 客户端开源地址:https://gitee.com/goalya/client4.git 具体运行界面请参考...

算法之名 ⋅ 23分钟前 ⋅ 0

设计模式--装饰者模式

装饰者模式 定义 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。 通用类图 意图 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比...

gaob2001 ⋅ 今天 ⋅ 0

JavaScript零基础入门——(八)JavaScript的数组

JavaScript零基础入门——(八)JavaScript的数组 欢迎大家回到我们的JavaScript零基础入门,上一节课我们讲了有关JavaScript正则表达式的相关知识点,便于大家更好的对字符串进行处理。这一...

JandenMa ⋅ 今天 ⋅ 0

sbt网络问题解决方案

转自:http://dblab.xmu.edu.cn/blog/maven-network-problem/ cd ~/.sbt/launchers/0.13.9unzip -q ./sbt-launch.jar 修改 vi sbt/sbt.boot.properties 增加一个oschina库地址: [reposit......

狐狸老侠 ⋅ 今天 ⋅ 0

大数据,必须掌握的10项顶级安全技术

我们看到越来越多的数据泄漏事故、勒索软件和其他类型的网络攻击,这使得安全成为一个热门话题。 去年,企业IT面临的威胁仍然处于非常高的水平,每天都会看到媒体报道大量数据泄漏事故和攻击...

p柯西 ⋅ 今天 ⋅ 0

Linux下安装配置Hadoop2.7.6

前提 安装jdk 下载 wget http://mirrors.hust.edu.cn/apache/hadoop/common/hadoop-2.7.6/hadoop-2.7.6.tar.gz 解压 配置 vim /etc/profile # 配置java环境变量 export JAVA_HOME=/opt/jdk1......

晨猫 ⋅ 今天 ⋅ 0

crontab工具介绍

crontab crontab 是一个用于设置周期性被执行的任务工具。 周期性执行的任务列表称为Cron Table crontab(选项)(参数) -e:编辑该用户的计时器设置; -l:列出该用户的计时器设置; -r:删除该...

Linux学习笔记 ⋅ 今天 ⋅ 0

深入Java多线程——Java内存模型深入(2)

5. final域的内存语义 5.1 final域的重排序规则 1.对于final域,编译器和处理器要遵守两个重排序规则: (1)在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用...

江左煤郎 ⋅ 今天 ⋅ 0

面试-正向代理和反向代理

面试-正向代理和反向代理 Nginx 是一个高性能的反向代理服务器,但同时也支持正向代理方式的配置。

秋日芒草 ⋅ 今天 ⋅ 0

Spring 依赖注入(DI)

1、Setter方法注入: 通过设置方法注入依赖。这种方法既简单又常用。 类中定义set()方法: public class HelloWorldOutput{ HelloWorld helloWorld; public void setHelloWorld...

霍淇滨 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部