weblogic quartz oracle rac,Spring之——quartz集群(Oracle數據源)

在集群環境下,大家會碰到一直困擾的問題,即多個 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 文件的內容 :

class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

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 的錯誤,原因在於:

execute

這個 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

class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

com.testcompany.framework.quartz.MyDetailQuartzJobBean

0/5 * * * * ?

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值