当前位置: 首页 > 编程日记 > 正文

quartz在集群环境下的最终解决方案

在集群环境下,大家会碰到一直困扰的问题,即多个 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" "www.springframework.org/dtd/spring-… ">

<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

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 类:

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" "www.springframework.org/dtd/spring-… ">

<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 的集群与自动错误冗余。


相关文章:

【通知】2021-2022-1线性代数课程答疑安排

2021-2022-1线性代数课程答疑安排 本学期线性代数课程答疑安排如下&#xff1a; 答疑时间&#xff1a;每周二 13&#xff1a;00-14&#xff1a;30&#xff1b;答疑地点&#xff1a;教七楼202&#xff08;信息教研室&#xff09;&#xff1b; 答疑教师排班如下: 第五周&…

解压zip_go|用Go写一个zip解压脚本

用服务器自带的unzip命令解压zip包时&#xff0c;经常遇到编码问题&#xff0c;所以用Go写一个zip解压脚本来处理zip包代码如下&#xff1a;package mainimport ("archive/zip""bytes""flag""fmt""io""io/ioutil"…

springmvc常用注解标签详解

参考&#xff1a;https://www.cnblogs.com/leskang/p/5445698.html 1、Controller 在SpringMVC 中&#xff0c;控制器Controller 负责处理由DispatcherServlet 分发的请求&#xff0c;它把用户请求的数据经过业务处理层处理之后封装成一个Model &#xff0c;然后再把该Model 返…

python基础学习-5(包与模块)

包和模块&#xff1a; 模块导入&#xff0c;会将模块(xxx.py编译为xxx.pyc,以便于下次直接使用) Python搜索模块的路径&#xff1a;1) 程序的主目录2) PTYHONPATH目录&#xff08;如果已经进行了设置&#xff09;3) 标准连接库目录&#xff08;一般在/usr/local/lib/python2…

【青少年编程】【四级】数字之和

「青少年编程竞赛交流群」已成立&#xff08;适合6至18周岁的青少年&#xff09;&#xff0c;公众号后台回复【Scratch】或【Python】&#xff0c;即可进入。如果加入了之前的社群不需要重复加入。 我们将有关编程题目的教学视频已经发布到抖音号21252972100&#xff0c;小马老…

ios uiview 如何刷新_UIView的重绘及布局刷新

本文将简要讨论以下几个问题&#xff1a;1、UIView的drawRect方法的调用机制及注意点2、UIView的layoutSubviews、layoutIfNeeded、setNeedsLayout等方法的调用机制3、如何通过更新view的约束值来实现动画效果博客配图重绘机制 - drawRect方法定义Drawing and Updating the Vie…

Ueditor和CKeditor 两款编辑器的使用与配置

一丶ueditor 百度编辑器 1.官方文档&#xff0c;演示&#xff0c;下载地址&#xff1a;http://ueditor.baidu.com/website/index.html 2.百度编辑器的好&#xff1a;Editor是由百度web前端研发部开发所见即所得富文本web编辑器&#xff0c;具有轻量&#xff0c;可定制&#xff…

Datawhale组队学习周报(第032周)

希望开设的开源内容 目前Datawhale的开源内容分为两种&#xff1a;第一种是已经囊括在我们的学习路线图内的Datawhale精品课&#xff0c;第二种是暂未囊括在我们的学习路线图内的Datawhale打磨课。我们根据您的投票来确定精品课程的排期&#xff0c;打磨课程一旦完成&#xff…

常建的性能指标

1、响应时间响应时间指的是“系统响应时间”&#xff0c;定义为应用系统从发出请求开始到客户端接收到响应所消耗的时间。把它作为用户视角的软件性能的主要体现。它包括网络上的传输时间&#xff0c;web服务器上处理时间&#xff0c;APP服务器上处理时间&#xff0c;DB服务器上…

东野圭吾最值得看的书排行榜_东野圭吾最值得看的7本作品,我进了坑就再也没出来...

自从读了东野圭吾的《解忧杂货店》,我便一发不可收拾,成了这位日本推理大佬的脑残粉。 有的人说不清他哪里好,但就是想戒也戒不掉!读了东野圭吾几乎所有作品后,我盘点了「东野圭吾作品必读top 7」,拿去不谢—— 07《圣女的救济》 一场真正的“完美谋杀”! 男子在家中死亡…

【青少年编程】【四级】用逗号分隔列表

「青少年编程竞赛交流群」已成立&#xff08;适合6至18周岁的青少年&#xff09;&#xff0c;公众号后台回复【Scratch】或【Python】&#xff0c;即可进入。如果加入了之前的社群不需要重复加入。 我们将有关编程题目的教学视频已经发布到抖音号21252972100&#xff0c;小马老…

Console-算法-一个偶数总能表示为两个素数之和

ylbtech-Arithmetic:Console-算法-一个偶数总能表示为两个素数之和1.A&#xff0c;Demo(案例)【程序84】题目&#xff1a;一个偶数总能表示为两个素数之和。1.程序分析&#xff1a; 1.B&#xff0c;Solution(解决方案)【不太理解】using System;namespace ConsoleApplication1 …

day16-筛选器以及Tab菜单示例

一、前言 之前我们学习dom写Tab菜单示例&#xff0c;今天我们来学习一下常用的筛选器和Tab菜单示例。 二、操作的html <head><meta charset"UTF-8"><title>Title</title><style>.header{background-color: black;color: white;cursor:…

nginx和mysql链接_nginx转发mysql连接

场景&#xff1a;访问UAT环境&#xff0c;只能使用客户电脑访问&#xff0c;太难用了&#xff0c;于是就需要在自己电脑上跑代码&#xff0c;通过客户电脑中转来访问uat环境的数据库。选用nginx进行转发。配置如下&#xff1a;stream {upstream cloudsocket {hash $remote_addr…

两个有序单链表的并交差运算

/*实验2.6&#xff1a;求集合&#xff08;有序单链表表示&#xff09;的并、交和差运算*/ #include<iostream> #include<malloc.h> using namespace std; typedef char ElemType; typedef struct LNode {ElemType data;struct LNode *next; }LinkList; void Displa…

【青少年编程】【蓝桥杯】排队购票

「青少年编程竞赛交流群」已成立&#xff08;适合6至18周岁的青少年&#xff09;&#xff0c;公众号后台回复【Scratch】或【Python】&#xff0c;即可进入。如果加入了之前的社群不需要重复加入。 我们将有关编程题目的教学视频已经发布到抖音号21252972100&#xff0c;小马老…

Linux (CentOS 7 )下搭建局域网SVN服务器+SVN权限配置

准备 公司内部需要配置局域网SVN&#xff0c;需要在在内部虚拟机服务器搭建&#xff0c;搭建过程做个记录&#xff0c;供参考。注&#xff1a;如果条件允许&#xff0c;尽量在windows下搭建svn服务器&#xff0c;很省事&#xff0c;尤其是权限配置非常方便又易懂&#xff0c;效…

mysql事件探查器_【干货】Mysql的事件探查器-之Mysql-Proxy代理实战一(安装部署与实战sql拦截与性能监控)...

1:资料参考https://blog.csdn.net/coldljy/article/details/3168906https://www.cnblogs.com/jwentest/p/8552075.htmlhttps://www.cnblogs.com/ExMan/p/10396298.html一&#xff1a;原理Mysql-Proxy是一个处于你的client端和Mysql Server端之间的一个简单程序&#xff0c;它可…

【青少年编程】【蓝桥杯】绘制莲花图形

「青少年编程竞赛交流群」已成立&#xff08;适合6至18周岁的青少年&#xff09;&#xff0c;公众号后台回复【Scratch】或【Python】&#xff0c;即可进入。如果加入了之前的社群不需要重复加入。 我们将有关编程题目的教学视频已经发布到抖音号21252972100&#xff0c;小马老…

问题总结两天来两场实习面试(中科创达、华为)

查了好多资料&#xff0c;发现还是不全&#xff0c;干脆自己整理吧&#xff0c;至少证保在我的做法正确的&#xff0c;以免误导读者&#xff0c;也是给自己做个记录吧&#xff01; 昨天下午去的中科创达口试 今天下午刚刚结束为华的练习生口试&#xff0c;月五二号去上机试笔&a…

不想被问年终奖?2018年春节自救攻略来了!

转眼间,春节即将来临&#xff01;当然按捺不住那颗归家的心~但是想到回家&#xff0c;就要接受来自七大姑八大姨的亲切问候&#xff0c;美好的假期变得不怎么美好了&#xff0c;瞬间忧伤起来~对象难找、年终奖少&#xff0c;当被七姑八姨问起时&#xff0c;内心总会产生抵触情绪…

mysql complete_mysql 无意重启 [Note] /usr/sbin/mysqld: Normal shutdown

情况&#xff1a;今早发现&#xff0c;昨天下午安装的4台mysql服务器&#xff0c;突然出现&#xff0c;由于在shell窗口(rootlocalhost:mysql.sock) [(none)]> 190102 18:12:16 mysqld_safe mysqld from pid file /home/data/mysqldata/3306/data/mysql3.pid ended什么情况&…

redis 数据结构 内存管理 持久化

为什么80%的码农都做不了架构师&#xff1f;>>> Redis 内存数据结构与编码 OBJECT encoding key、DEBUG OBJECT key简单动态字符串&#xff08;simple dynamic string&#xff09;链表&#xff08;linked list&#xff09;字典&#xff08;dict&#xff09;跳表&am…

【青少年编程】【蓝桥杯】水仙花数

「青少年编程竞赛交流群」已成立&#xff08;适合6至18周岁的青少年&#xff09;&#xff0c;公众号后台回复【Scratch】或【Python】&#xff0c;即可进入。如果加入了之前的社群不需要重复加入。 我们将有关编程题目的教学视频已经发布到抖音号21252972100&#xff0c;小马老…

本地 无法启动 SQL Server 错误代码126

Windows 不能在 本地计算机 启动 SQL Server 。错误代码126. 2008 R2 配置工具 VIA设置禁用。 然后重启一下服务 转载于:https://www.cnblogs.com/wuxinyu/archive/2013/05/08/3068008.html

mysql 表的继承,MySQL是否支持表继承?

I have this code in PostgreSQLCREATE TABLE first (id serial,primary key(id));CREATE TABLE second (primary key(id)) INHERITS (first);What is the equivalent code for MySQL?解决方案MySQL does not support table inheritance. The only way to approximate the fun…

C#编程概念系列(一):面向对象编程

系列文章索引目录&#xff1a;http://www.cnblogs.com/loner/archive/2013/05/09/3068211.html 引子&#xff1a; 面向对象编程&#xff1a;这个在当下已不是什么时髦的概念&#xff0c;但通过自己的语言组织描述出来可能会有点意外的收获&#xff01; 记得最近看的乔布斯在199…

【青少年编程】【四级】数字反转

「青少年编程竞赛交流群」已成立&#xff08;适合6至18周岁的青少年&#xff09;&#xff0c;公众号后台回复【Scratch】或【Python】&#xff0c;即可进入。如果加入了之前的社群不需要重复加入。 我们将有关编程题目的教学视频已经发布到抖音号21252972100&#xff0c;小马老…

【对讲机的那点事】关于对讲机锂电池你了解多少?

在对讲机日常使用中&#xff0c;对讲机电池是属于损耗件&#xff0c;随着使用时间、频率的增加会减弱其使用性能&#xff1b; 锂离子聚合物电池成为领先的电池技术&#xff0c;它具有不容忽视的优势&#xff0c;但是你究竟对锂离子电池有多少了解呢&#xff1f; 较高的电能重量…

mac mysql5.7 my_【mysql】Mac下安装mysql5.7 完整步骤,大坑已解决

最近使用Mac系统&#xff0c;准备搭建一套本地web服务器环境。因为Mac系统自带PHP和apach&#xff0c;但是没有自带mysql&#xff0c;所以要手动去安装mysql&#xff0c;本次安装mysql最新版5.7.17。1.官网下载点击上面的地址&#xff0c;会看到如下图的页面。你可能不知道该下…