转载请注明出处:http://blog.csdn.net/l1028386804/article/details/49128155

在集群环境下,大家会碰到一直困扰的问题,即多个 APP 下如何用 quartz 协调处理自动化 JOB 。

大家想象一下,现在有 A , B , C3 台机器同时作为集群服务器对外统一提供 SERVICE :
A , B , C 3 台机器上各有一个 QUARTZ ,他们会按照即定的 SCHEDULE 自动执行各自的任务。
我们先不说实现什么功能,就说这样的架构其实有点像多线程。
那多线程里就会存在“资源竞争”的问题,即可能产生脏读,脏写,由于三台 APP SERVER 里都有 QUARTZ ,因此会存在重复处理 TASK 的现象。
一般外面的解决方案是只在一台 APP 上装 QUARTZ ,其它两台不装,这样集群就形同虚设了;
另一种解决方案是动代码,这样就要影响到原来已经写好的 QUARTZ JOB 的代码了,这对程序开发人员来说比较痛苦;

本人仔细看了一下 Spring 的结构和 QUARTZ 的文档,结合 Quartz 自身可以实例化进数据的特性找到了相关的解决方案。

本方案优点:

1.每台作为集群点的 APP SERVER 上都可以布署 QUARTZ ;
2.QUARTZ 的 TASK ( 12 张表)实例化如数据库,基于数据库引擎及 High-Available 的策略(集群的一种策略)自动协调每个节点的 QUARTZ ,当任一一节点的 QUARTZ 非正常关闭或出错时,另几个节点的 QUARTZ 会自动启动;
3.无需开发人员更改原已经实现的 QUARTZ ,使用 SPRING+ 类反射的机制对原有程序作切面重构;
本人也事先搜索了一些资料,发觉所有目前在 GOOGLE 上或者在各大论坛里提供的解决方案,要么是只解决了一部分,要么是错误的,要么是版本太老,要么就是完全抄别人的。
尤其是在使用 QUARTZ+SPRING 对数据库对象作实例化时会抛错(源于 SPRING 的一个 BUG ),目前网上的解决方案全部是错的或者干脆没说,本人在此方案中也会提出如何解决。

解决方案:

1.把 QUARTZ 的 TASK 实例化进数据库, QUARTZ 只有实例化进入数据库后才能做集群,外面的解决方案说实例化在内存里全部是错的,把quartz-1.8.4/docs/dbTables/tables_oracle.sql 在 ORACLE9I2 及以上版本中执行一下会生成 12 张表;
2.生成 quartz.properties 文件,把它放在工程的 src 目录下,使其能够被编译时纳入 class path 。
一般我们的开发人员都喜欢使用 SPRING+QUARTZ ,因此这个 quartz.properties 都不用怎么去写,但是在集群方案中 quartz.properties 必写,如果不写 quartz 会调用自身 jar 包中的 quartz.properties 作为默认属性文件,同时修改 quartz.xml 文件。

Quartz.xml 文件的内容 :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd ">
<beans><bean id="mapScheduler" lazy-init="false" autowire="no"class="org.springframework.scheduling.quartz.SchedulerFactoryBean"><property name="configLocation" value="classpath:quartz.properties" /><property name="triggers"><list><ref bean="cronTrigger" /></list></property><!—- 就是下面这句,因为该 bean 只能使用类反射来重构--><property name="applicationContextSchedulerContextKey" value="applicationContext" /></bean> 

quartz.properties 文件的内容:

基于Oracle的配置

org.quartz.scheduler.instanceName = mapScheduler
org.quartz.scheduler.instanceId = AUTO
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.oracle.weblogic.WebLogicOracleDelegate
org.quartz.jobStore.dataSource = myXADS
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
org.quartz.dataSource.myXADS.jndiURL=jdbc/TestQuartzDS
org.quartz.dataSource.myXADS.jndiAlwaysLookup = DB_JNDI_ALWAYS_LOOKUP
org.quartz.dataSource.myXADS.java.naming.factory.initial = weblogic.jndi.WLInitialContextFactory
org.quartz.dataSource.myXADS.java.naming.provider.url = t3://localhost:7020
org.quartz.dataSource.myXADS.java.naming.security.principal = weblogic
org.quartz.dataSource.myXADS.java.naming.security.credentials = weblogic 

3.重写 quartz 的 QuartzJobBean 类

原因是在使用 quartz+spring 把 quartz 的 task 实例化进入数据库时,会产生: serializable 的错误,原因在于:

<bean id="jobtask" class="org.springframework.scheduling.quartz. MethodInvokingJobDetailFactoryBean "><property name="targetObject"><ref bean="quartzJob"/></property><property name="targetMethod"><value>execute</value></property>
</bean>

这个 MethodInvokingJobDetailFactoryBean 类中的 methodInvoking 方法,是不支持序列化的,因此在把 QUARTZ 的 TASK 序列化进入数据库时就会抛错。网上有说把 SPRING 源码拿来,修改一下这个方案,然后再打包成 SPRING.jar 发布,这些都是不好的方法,是不安全的。
必须根据 QuartzJobBean 来重写一个自己的类,然后使用 SPRING 把这个重写的类(我们就名命它为: MyDetailQuartzJobBean )注入 appContext 中后,再使用 AOP 技术反射出原有的 quartzJobx( 就是开发人员原来已经做好的用于执行 QUARTZ 的 JOB 的执行类 ) 。
下面来看 MyDetailQuartzJobBean 类:

package com.cdsmartlink.spring.test;import java.lang.reflect.Method;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.QuartzJobBean;public class MyDetailQuartzJobBean extends QuartzJobBean {protected final Log logger = LogFactory.getLog(getClass());private String targetObject;private String targetMethod;private ApplicationContext ctx;protected void executeInternal(JobExecutionContext context) throws JobExecutionException {try {logger.info("execute [" + targetObject + "] at once>>>>>>");Object otargetObject = ctx.getBean(targetObject);Method m = null;try {m = otargetObject.getClass().getMethod(targetMethod,new Class[] {JobExecutionContext.class});m.invoke(otargetObject, new Object[] {context});} catch (SecurityException e) {logger.error(e);} catch (NoSuchMethodException e) {logger.error(e);}} catch (Exception e) {throw new JobExecutionException(e);}}public void setApplicationContext(ApplicationContext applicationContext) {this.ctx = applicationContext;}public void setTargetObject(String targetObject) {this.targetObject = targetObject;}public void setTargetMethod(String targetMethod) {this.targetMethod = targetMethod;}
}

再来看完整的 quartz.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd ">
<beans><bean id="mapScheduler" lazy-init="false" autowire="no"class="org.springframework.scheduling.quartz.SchedulerFactoryBean"><property name="configLocation" value="classpath:quartz.properties" /><property name="triggers"><list><ref bean="cronTrigger" /></list></property><property name="applicationContextSchedulerContextKey" value="applicationContext" /></bean><bean id="quartzJob" class="com.testcompany.framework.quartz.QuartzJob"></bean><bean id="jobTask" class="org.springframework.scheduling.quartz.JobDetailBean"><property name="jobClass"><value>com.testcompany.framework.quartz.MyDetailQuartzJobBean </value></property><property name="jobDataAsMap"><map><entry key="quartzJob" value="quartzJob" /><entry key="targetMethod" value="execute" /></map></property></bean><bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"><property name="jobDetail"><ref bean="jobTask" /></property><property name="cronExpression"><value>0/5 * * * * ?</value></property></bean>
</beans>

4.下载最新的 quartz1.8 版,把 quartz-all-1.8.4.jar, quartz-oracle-1.8.4.jar,quartz-weblogic-1.8.4.jar 这三个包放到 web-inf/lib 目录下,布署。

测试:

几个节点都带有 quartz 任务,此时只有一台 quartz 在运行,另几个节点上的 quartz 没有运行。
此时手动 shutdown 那台运行 QUARTZ (在程序里加 system.out.println(“execute once…”), 运行 quartz 的那个节点在后台会打印 execute once )的节点,过了 7 秒左右,另一个节点的 quartz 自动监测到了集群中运行着的 quartz 的 instance 已经 shutdown,因此 quartz 集群会自动把任一台可用的 APP 上启动起一个 quartz job 的任务。
自此, QUARTZ 使用 HA 策略的集群大功告成,不用改原有代码,配置一下我们就可作到 QUARTZ 的集群与自动错误冗余。

注:本文的quartz版本为Quartz1.x

Spring之——quartz集群(Oracle数据源)相关推荐

  1. Spring整合Quartz集群部署

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

  2. quartz mysql数据源_Spring之——quartz集群(MySQL数据源)

    1 大家都清楚quartz最基本的概念就是job,在job内调用具体service完成具体功能,quartz需要把每个job存储起来,方便调度,quartz存储job方式就分三种,我们最常用的也是qu ...

  3. Springboot整合Quartz集群部署以及配置Druid数据源

    参考链接: https://blog.csdn.net/wangmx1993328/article/details/105441308 https://blog.csdn.net/qq_3966905 ...

  4. Spring+Quartz 集群

    转载 http://soulshard.iteye.com/blog/337886 单独的Quartz集群在http://unmi.blogjava.net/有Unmi翻译的Quartz Job Sc ...

  5. Spring中定时任务Quartz集群配置学习

    Spring中定时任务Quartz集群配置学习 原来配置的Quartz是通过spring配置文件生效的,发现在非集群式的服务器上运行良好,但是将工程部署到水平集群服务器上去后改定时功能不能正常运行,没 ...

  6. Spring Boot2.x 整合quartz集群

    springboot2.x支持对quartz的自动配置,引入jar <!-- spring boot2.x + quartz --><dependency><groupI ...

  7. quartz集群分布式(并发)部署解决方案-Spring - 推酷

    quartz集群分布式(并发)部署解决方案-Spring - 推酷 quartz集群分布式(并发)部署解决方案-Spring - 推酷 posted on 2015-09-05 16:03 lexus ...

  8. 项目中使用Quartz集群分享--转载

    原文:http://hot66hot.iteye.com/blog/1726143 在公司分享了Quartz,发布出来,希望大家讨论补充. CRM使用Quartz集群分享  一:CRM对定时任务的依赖 ...

  9. java集群调度_集群环境下定时调度的解决方案之Quartz集群

    集群环境可能出现的问题 在上一篇博客我们介绍了如何在自己的项目中从无到有的添加了Quartz定时调度引擎,其实就是一个Quartz 和Spring的整合过程,很容易实现,但是我们现在企业中项目通常都是 ...

最新文章

  1. 73. Leetcode 230. 二叉搜索树中第K小的元素 (二叉搜索树-中序遍历类)
  2. 基于matlab的捷联惯导算法设计及仿真,基于 Matlab 的捷联惯导算法设计及仿真1doc.doc...
  3. 【数据结构与算法】之深入解析“石子游戏IV”的求解思路与算法示例
  4. 25个优秀的设计机构网站设计案例
  5. 1.2 User Interface 规范(系统开发架构与设计步步谈)
  6. 运维常用进程查看命令
  7. python函数与方法的区别_python中函数和方法有哪些区别
  8. Spring Boot 前端传json数组,后台接收处理
  9. eclipse配置tomcat运行时访问路径不要项目名称
  10. 面向对象---基础概念
  11. mysql server 6.0_MySQL Server 6.0完全卸载以及卸载中遇到的问题
  12. 30分钟用flex测试FDS
  13. SQL server2016 数据库 基础知识
  14. js文本框设置必填项_input必填_js 检验必填项的代码
  15. Python 批量更改文件名、更改文件格式
  16. 两年的前端程序员的反省与总结
  17. 【题解】CF760B:Frodo and pillows
  18. 基于stm32单片机的空气质量检测仿真(仿真+源码+全套资料)
  19. Python代码画樱花树--Turtle绘图
  20. Jackknife法和bootstrap法(自助法与刀切法)

热门文章

  1. 恢复硬盘数据了,,,
  2. ds18b20温度转换指令_51单片机驱动DS18B20温度传感器程序及心得
  3. 四大常见的jQuery选择器+案例
  4. 拐卖儿童是否该一律判死刑”你的观点是?
  5. Syntax Error: Error: PostCSS received undefined instead of CSS string
  6. 使用Go编写的高效批量Ping工具:gping
  7. (树形dp)Binary Apple Tree
  8. 爬虫周学习总结__赵俊杰
  9. 【题解】POJ 1852 Ants(搜索)
  10. 里程碑4刷linux,里程碑3刷机包 里程碑4官方刷机包