JobDataMap被用来保存一系列的(序列化的)对象,这些对象在Job执行时可以得到。JobDataMap是Java Map接口的一个实现,而且还增加了一些存储和读取主类型数据的便捷方法。
如果使用一个持久的JobStore,那么必须注意存放在JobDataMap中的内容。因为放入JobDataMap中的内容将被序列化,而且
容易出现类型转换问题。很明显,标准Java类型将是非常安全的,但除此之外的类型,任何时候,只要有人改变了你要序列化其实例的类的定义,就要注意是否
打破了程序的兼容性。另外,你可以对JobStore和JobDataMap采用一种使用模式:就是只把主类型和String类型存放在Map中,这样就
可以减少后面序列化的问题。

Triggers也可以有JobDataMaps与之相关联。当scheduler中的Job被多个有规律或者重复触发的Triggers所使用时非常有用。对于每次独立的触发,你可为Job提供不同的输入数据。

从Job执行时的JobExecutionContext中取得JobDataMap是惯用手段,它融合了从JobDetail和从Trigger中获的JobDataMap,当有相同名字的键时,它用后者的值覆盖前者值。

静态处理方式:

在集群环境下,大家会碰到一直困扰的问题,即多个 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 文件的内容:

  • 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

  1. 重写 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 类:

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[] {});

m.invoke(otargetObject, new Object[] {});

} 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>

  1. 下载最新的
    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
的集群与自动错误冗余。

JobDataMap 不能被序列化如何解决研究中的更多相关文章

  1. 解决eclipse中自带的maven搜索不到非本地第三方包问题

     解决eclipse中自带的maven搜索不到非本地第三方包问题   版权声明:本文为博主原创文章,未经博主允许不得转载. 最近使用eclipse中的maven插件时发现,在pom.xml文件中添加第 ...

  2. Html5 中获取镜像图像 - 解决 WebGL 中纹理倒置问题

    Html5 中获取镜像图像 - 解决 WebGL 中纹理倒置问题 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致& ...

  3. Data-independent acquisition mass spectrometry in metaproteomics of gut microbiota - implementation and computational analysis DIA技术在肠道宏蛋白质组研究中的方法实现和数据分析 (解读人:闫克强)

    文献名:Data-independent acquisition mass spectrometry in metaproteomics of gut microbiota - implementat ...

  4. Domain Adaptive Faster R-CNN:经典域自适应目标检测算法,解决现实中痛点,代码开源 | CVPR2018

    论文从理论的角度出发,对目标检测的域自适应问题进行了深入的研究,基于H-divergence的对抗训练提出了DA Faster R-CNN,从图片级和实例级两种角度进行域对齐,并且加入一致性正则化来学 ...

  5. iOS 解决LaunchScreen中图片加载黑屏问题

    iOS 解决LaunchScreen中图片加载黑屏问题 原文: http://blog.csdn.net/chengkaizone/article/details/50478045 iOS 解决Lau ...

  6. 解决Android中No resource found that matches android:TextAppearance.Material.Widget.Button.Inverse问题

    解决Android中No resource found that matches android:TextAppearance.Material.Widget.Button.Inverse问题http ...

  7. geotrellis使用(十六)使用缓冲区分析的方式解决投影变换中边缘数据值计算的问题

    Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html 目录 前言 问题探索 采样说明 实现方案 总结 一.前言     ...

  8. 解决MyEclipse中的js报错的小方法

    今天,下了个模版,但是导进去的时候发现js会报错.看了下其他都没有错误.而有一个js报错误,请原谅我有点红色强迫症,不能留一点红色 . 错误如下:Syntax error on token " ...

  9. 解决MVC中JSON字符长度超出限制的异常

    解决MVC中JSON字符长度超出限制的异常 解决方法如下: <configuration> <system.web.extensions> <scripting> ...

随机推荐

  1. Aspose.Words 自定义文档模版生成操作类

    /// <summary> /// 操作word通用类 LIYOUMING add 2017-12-27 /// </summary> public class DocHelp ...

  2. 拥抱 Android Studio 之一:从 ADT 到 Android Studio

    http://kvh.io/cn/embrace-android-studio-migration.html 1. 拥抱变化,拥抱新事物 Android Studio(IntelliJ IDEA)vs ...

  3. Hive(十)Hive性能调优总结

    一.Fetch抓取 1.理论分析 Fetch抓取是指,Hive中对某些情况的查询可以不必使用MapReduce计算.例如:SELECT * FROM employees;在这种情况下,Hive可以简单 ...

  4. mac系统编译安装ImageMagick7.0.1-3

    1.下载源码包: git clone http://git.imagemagick.org/repos/ImageMagick.git 2.编译安装: cd ImageMagick/ ./config ...

  5. 易普优高级计划排程Light版助力中小企业实现精益化计划

    易普优高级计划排程Light版助力中小企业实现精益化计划 一.业务与排产需求 根据统计,目前中小企业已经占到我国工业企业总数的95%以上,对中国GDP贡献超过60%,税收超过了50%,提供了70%的进 ...

  6. JS学习笔记(一)DOM事件和监听

    将事件绑定到元素身上的三种方法: 1.HTML事件处理程序(不推荐使用) 1 <a onclick="hide()"> 2.传统的DOM事件处理程序 即在目标DOM事件 ...

  7. iconfont 在项目中的简单使用

    font-class引用 font-class是unicode使用方式的一种变种,主要是解决unicode书写不直观,语意不明确的问题. 与unicode使用方式相比,具有如下特点: 兼容性良好,支持 ...

  8. IOS常用第三方类库

    开发几个常用的开源类库及下载地址: 1.json json编码解码 2.GTMBase64 base64编码解码 3.TouchXML xml解析 4.SFHFKeychainUtils 安全保存用户 ...

  9. Linux signal 编程(转载)

    转载地址:http://blog.sina.com.cn/s/blog_4b226b92010119l5.html 当服务器close一个连接时,若client端接着发数据.根据TCP协议的规定,会收 ...

  10. Win10 重装后,必须修改的设置

    作为一个程序猿,系统易用性是相当重要,每次重装WIN10 都会遇到一头包的问题,比如不能远程,打开文件各种提示需要管理员权限(mlgb很想骂人,我明明是管理员权限) ,然后开了管理员权限,结果又不能用 ...