之前已经把功能基本都实现了,这里我们再来优化一下代码。

我们发现,在创建、修改、和删除定时任务时,对于quartz的操作其实是可以封装成一个简单的工具辅助类的,如创建的代码可以抽取成:

 

/**

* 创建定时任务

*

* @param scheduler the scheduler

* @param jobName the job name

* @param jobGroup the job group

* @param cronExpression the cron expression

* @param isSync the is sync

* @param param the param

*/

public static void createScheduleJob(Scheduler scheduler, String jobName, String jobGroup,

String cronExpression, boolean isSync, Object param) {

//同步或异步

Class<? extends Job> jobClass = isSync ? JobSyncFactory.class : JobFactory.class;

//构建job信息

JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).build();

//放入参数,运行时的方法可以获取

jobDetail.getJobDataMap().put(ScheduleJobVo.JOB_PARAM_KEY, param);

//表达式调度构建器

CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

//按新的cronExpression表达式构建一个新的trigger

CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup)

.withSchedule(scheduleBuilder).build();

try {

scheduler.scheduleJob(jobDetail, trigger);

} catch (SchedulerException e) {

LOG.error("创建定时任务失败", e);

throw new ScheduleException("创建定时任务失败");

}

}

把任务的具体信息包括Scheduler都使用参数方式传入。

看过前面文章的同学或许还记得,quartz在spring中需要声明的对象只剩下一行:

 
  1. <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" />

既然这个schedulerFactoryBean只是在spring中声明一下,并没有做特殊的操作,在辅助的工具类中直接使用单例模式创建一个不是更好,还能少传一个参数?

你想的没错,这种方式的确更好还能解耦,但是我们来看一下SchedulerFactoryBean类的代码:

 

public class SchedulerFactoryBean extends SchedulerAccessor implements FactoryBean<Scheduler>, BeanNameAware, ApplicationContextAware, InitializingBean, DisposableBean, SmartLifecycle {

//......

}

它实现了spring的FactoryBean接口,也就是说它在创建时并不是简单的new而已,还夹杂了一些其它的复杂行为,所以我们也没必要特地的去怎么怎么样,还是在spring中声明一下吧。

另外,我们看它的getObject()方法:

 

public Scheduler getObject() {

return this.scheduler;

}

发现它实际返回的已经是Scheduler对象,既然如此在我们类中就不必注入schedulerFactoryBean再调用getScheduler()这么麻烦了,可以直接声明Scheduler对象:

 

@Service

public class ScheduleJobServiceImpl implements ScheduleJobService {

/** 调度Bean */

@Autowired

private Scheduler scheduler;

//......

}

当然你注入schedulerFactoryBean也不会有错,看过spring源码的同学应该立马就能明白这是getBean("bean")和getBean("&bean")的区别了。

另外说说前面漏掉的两个地方。

一、更新任务

先前我们在更新任务时,虽然更新了定时任务的执行时间,但是并没有对参数进行更新,即使用context.getMergedJobDataMap().get(...)方法获取到的参数还是旧的。

假设我们更新了任务的时间表达式,任务已按新的时间表达式在执行,但在获取到参数后发现时间表达式还是原来的。

尝试对参数进行更新,使用如下代码:

 

JobDetail jobDetail = scheduler.getJobDetail(getJobKey(jobName, jobGroup));

//jobDetail = jobDetail.getJobBuilder().ofType(jobClass).build();

//更新参数 实际测试中发现无法更新

JobDataMap jobDataMap = jobDetail.getJobDataMap();

jobDataMap.put(ScheduleJobVo.JOB_PARAM_KEY, param);

jobDetail.getJobBuilder().usingJobData(jobDataMap);

发现无法更新,试过其它几个api发现都不行,没有办法,最后采用了先删除任务再进行创建的方式来迂回实现参数的更新。demo中更新任务有直接修改方式和删除修改方式,区别就在这里。

二、任务的同步和异步

同步和异步在quartz 2.2的版本中对于使用者来说区别只在于是否在job类上添加了@DisallowConcurrentExecution注解。

按时这个特点我们建立两个job的实现工厂类,在其中一个类上添加注解@DisallowConcurrentExecution,然后可以根据添加任务时的参数来确定具体使用哪个:

 
  1. //同步或异步
  2. Class<? extends Job> jobClass = isSync ? JobSyncFactory.class : JobFactory.class;

需要注意在定时任务运行时更新是没有办法改变同步和异步的。

以下是最新整理的demo图:

接下来说说我在整合使用时碰到的一些已知问题。

一、更新任务时参数问题。也就是前面说的无法更新任务中传入的参数。

二、同步或异步在定时任务运行时修改是不能改变的,这个在前面也提到了。

三、在定时任务运行时修改,可能会该让任务长时间处于线程阻塞状态,即BLOCKED状态,即使你的任务中只有简单的一行System.out输出。要使它恢复也很简单,删除重建即可。

四、定时任务运行两次的问题。这个也是网上传的最多的问题,这里来着重的说一下。

网上流传引起该问题的原因目前主要有两个说法:

1 spring配置文件加载了多次,导致quartz的bean被实例化多次而导致任务多次执行。

2 tomcat的webapps目录问题。tomcat运行时加载了两次配置文件导致任务多次执行。

这两个说法在我的demo中应该并不存在,但为了验证我也尝试了下。

不使用tomcat,在main方法中用编程的方式启动spring,甚至不使用spring,直接用quartz官方给出的代码:

 

try {

// Grab the Scheduler instance from the Factory

Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

// and start it off

scheduler.start();

scheduler.shutdown();

} catch (SchedulerException se) {

se.printStackTrace();

}

问题还是存在,这就说明不是配置文件加载的问题了,这应该是quartz本身存在的一个bug,而且这个多次运行是很有规律的,基本按如下套路走:

  • 定为5秒运行一次,一切正常,没有多次执行现象发生。
  • 定为10秒运行一次,一切正常,没有多次执行现象发生。
  • 定为29秒运行一次,运行时一次正常,一次不正常。
  • 定为59秒运行一次,运行时一次正常,一次不正常。

以上是我实测得出的,再长时间就没测了,毕竟太耗时。在有运行两次的现象时都是间隔的,即一次正常一次不正常这种方式。

既然推断是quartz本身存在bug,那我们又要如何解决这个问题了?

其实在我个人看来,这个问题是无关紧要的,为什么说无关紧要呢?这就涉及到你项目业务设计的是否完善,代码是否健壮了。

一个设计良好的业务方法,特别是那些供外部调用的接口或方法,应该都支持幂等性,何为幂等性?即这个方法同样的参数至少在一个时间区间内,我调用1次和调用10次100次,结果都是一样的。

支持了幂等性,前面说的运行两次的情况是不是就无关紧要了?在有些定时任务为分布式设计的系统(后面会探讨)中,为了确保定时任务的执行甚至会故意人为的去调用两次。

当然支持幂等性最好是在进入方法时就判断,发现已经执行过时就立即返回而不是真的再去同样的结果再执行一遍,以节省资源。

转载于:https://my.oschina.net/u/1241094/blog/708810

Spring 整合Quartz 2实现定时任务四:细化调整及一些已知的问题相关推荐

  1. Spring 整合Quartz 2实现定时任务五:集群、分布式架构实现探讨

    到这里,功能上我们已经全实现了. 但是有时候我们的项目不是部署在一台机器上的,而是一个集群环境,往往我们的定时任务只需要一台机器执行就够了. 那么我们怎么样来实现这种集群环境下的定时任务运行呢? 前面 ...

  2. spring整合quartz框架

    前言: 在一些项目中,往往需要定时的去执行一些任务,比如商城项目,每天0点去统计前一天的销量.那么如何实现呢,总不可能我们每天0点手动的去执行统计销量的方法吧,这时就quartz就起作用了. quar ...

  3. 使用Spring整合Quartz轻松完成定时任务

    一.背景 上次我们介绍了如何使用Spring Task进行完成定时任务的编写,这次我们使用Spring整合Quartz的方式来再一次实现定时任务的开发,以下奉上开发步骤及注意事项等. 二.开发环境及必 ...

  4. Spring 整合 Quartz 分布式调度

    转载自 Spring 整合 Quartz 分布式调度 本文旨在对 Spring+Quartz 分布式调度有一个直观的了解,通过实际的使用来解决问题. 前言 为了保证应用的高可用和高并发性,一般都会部署 ...

  5. Spring整合Quartz集群部署

    Spring整合Quartz集群部署 Quartz的分布式模式 数据表创建 quartz.properties spring-job.xml Quartz的分布式模式 集群中的每个节点都是一个独立的Q ...

  6. Spring 3整合Quartz 2实现定时任务三:动态暂停 恢复 修改和删除任务

    前面我们已经完成了spring 3和quartz 2的整合以及动态添加定时任务,我们接着来完善它,使之能支持更多的操作,例如暂停.恢复.修改等. 在动态添加定时任务中其实已经涉及到了其中的一些代码,这 ...

  7. java 定时任务插件_详解Spring整合Quartz实现动态定时任务

    最近项目中需要用到定时任务的功能,虽然spring 也自带了一个轻量级的定时任务实现,但感觉不够灵活,功能也不够强大.在考虑之后,决定整合更为专业的Quartz来实现定时任务功能. 普通定时任务 首先 ...

  8. spring整合quartz框架定时任务实战

    Spring整合定时任务调度框架Quartz实战 2014-09-12 11:51 9707人阅读 评论(6) 收藏 举报  分类: [综合问题](74)  版权声明:本文为博主原创文章,未经博主允许 ...

  9. Spring整合Quartz实现定时任务

    Quartz框架是一个极其成功的开源任务调度框架,目前很多公司在实现任务调度上都直接使用或在借鉴的基础上实现了自己的任务调度框架,Quartz使用Trigger,Job和JobDetail对象实现调度 ...

最新文章

  1. 每日一皮:和女朋友争吵与阅读软件许可协议之间的共同点...
  2. (转)nginx+iis实现负载均衡
  3. VB:如何隐藏/显示treeview的ToolTips
  4. cmake / CMAKE _ * _ OUTPUT_DIRECTORY 说明
  5. Linux第四章自测习题——Linux系列学习笔记
  6. XCTF-高手进阶区:php_rce
  7. 与OutOfMemoryError相关的JVM参数
  8. Java 多线程异常捕获Runnable实现
  9. 本次安装visual studio所用的安装程序不完整_阁楼影院安装案例
  10. 三台服务器的时间同步-Linux
  11. chrome frame使用记录
  12. 真假屏幕测试软件,如何判断4K电视真假?测试方法在这里,最简单用手机就可以...
  13. debian8.4安装sqliteman总结
  14. C语言求金蝉素数,回文数 - 寂寞暴走伤的个人空间 - OSCHINA - 中文开源技术交流社区...
  15. 小srf的游戏(单调队列+dp)
  16. Java ArrayList集合案例(上课笔记)
  17. 『MongoDB』MongoDB的数据存储格式Bson比Json有哪些优势?
  18. 七夕常用的shell表白脚本
  19. elasticsearch学习(六):IK分词器
  20. Qt编写可视化大屏电子看板系统11-自定义控件

热门文章

  1. markdown字体大小设置_Markdown 颜色和字体
  2. 背景建模(一) Evaluation of Background Subtraction Techniques
  3. 一位创业者创业失败后,成功的做了一个创业孵化器!
  4. sax dom html解析xml,Python通过DOM和SAX方式解析XML的应用实例分享
  5. java dscape_包含Java脚本的Python漂亮的Soup scape页面
  6. solidity 中的时间_solidity基础知识
  7. 最后一次作业-- 总结报告
  8. Faiss(17):IndexIVFPQ文件格式分析
  9. Android绘图(一)基础篇
  10. kotlin核心编程pdf下载_《Kotlin核心编程》 ——1.2.3 简单却不容易