java集群调度_集群环境下定时调度的解决方案之Quartz集群
集群环境可能出现的问题
在上一篇博客我们介绍了如何在自己的项目中从无到有的添加了Quartz定时调度引擎,其实就是一个Quartz 和Spring的整合过程,很容易实现,但是我们现在企业中项目通常都是部署在集群环境中的,这样我们之前的定时调度就会出现问题了,因为我们的定时任务都加载在内存中的,每个集群节点中的调度器都会去执行,这就会存在重复执行和资源竞争的问题,那么如何来解决这样的问题呢,往下面看吧...
解决方案
在一般的企业中解决类似的问题一般都是在一个note上部署Quartz其他note不部署(或者是在其他几个机器加IP地址过滤),但是这样集群对于定时任务来说就没有什么意义了,而且存在着单点故障的隐患,也就是这台部署着Quartz的机器一旦挂了,我们的定时任务就停止服务了,这绝对不是我们想要的。
Quartz本身是支持集群的,我们通过Quartz的集群方式来解决这样的问题。
Quartz集群
虽然单个 Quartz 实例能给予你很好的 Job调度能力,但它不能令典型的企业需求,如可伸缩性、高可靠性满足。假如你需要故障转移的能力并能运行日益增多的 Job,Quartz 集群势必成为你方言的一部分了,并且即使是其中一台机器在最糟的时间崩溃了也能确保所有的 Job 得到执行。 QuartzJob Scheduling Framework
了解了Quartz集群的好处,接下来就对我们之前的工程进行改造,增加Quartz集群特性。
Quartz集群中节点依赖于数据库来传播 Scheduler 实例的状态,你只能在使用 JDBC JobStore 时应用 Quartz 集群
所以我们集群的第一步就是建立Quartz所需要的12张表:
1、建表
在quartz核心包里面通过quartz提供的建表语句建立相关表结构
生成的表结构如下
这几张表是用于存储任务信息,触发器,调度器,集群节点等信息
详细解释:
QRTZ_CRON_TRIGGERS 存储Cron Trigger,包括Cron 表达式和时区信息
QRTZ_PAUSED_TRIGGER_GRPS 存储已暂停的Trigger 组的信息
QRTZ_LOCKS 存储程序的非观锁的信息(假如使用了悲观锁)
QRTZ_JOB_LISTENERS 存储有关已配置的JobListener 的信息
QRTZ_BLOG_TRIGGERS Trigger 作为Blob 类型存储(用于Quartz 用户用JDBC 创建他们自己定制的Trigger 类型,JobStore并不知道如何存储实例的时候)
QRTZ_TRIGGERS 存储已配置的Trigger 的信息
所有的表默认以前缀QRTZ_开始。可以通过在quartz.properties配置修改(org.quartz.jobStore.tablePrefix= QRTZ_)。
2、编写quartz.properties文件
建立 quartz.properties文件把它放在工程的 src 目录下,内容如下:
1 #============================================================================
2
3 # Configure Main Scheduler Properties
4
5 #============================================================================
6
7
8
9 org.quartz.scheduler.instanceName = Mscheduler
10
11 org.quartz.scheduler.instanceId = AUTO
12
13 org.quartz.jobStore.clusterCheckinInterval=20000
14
15
16
17 #============================================================================
18
19 # Configure ThreadPool
20
21 #============================================================================
22
23
24
25 org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
26
27 org.quartz.threadPool.threadCount = 3
28
29 org.quartz.threadPool.threadPriority = 5
30
31
32
33 #============================================================================
34
35 # Configure JobStore
36
37 #============================================================================
38
39
40
41 #org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
42
43
44
45 org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
46
47 #org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
48
49 org.quartz.jobStore.useProperties = true
50
51 #org.quartz.jobStore.dataSource = myDS
52
53 org.quartz.jobStore.tablePrefix = QRTZ_
54
55 org.quartz.jobStore.isClustered = true
56
57 org.quartz.jobStore.maxMisfiresToHandleAtATime=1
58
59 #============================================================================
60
61 # Configure Datasources
62
63 #============================================================================
64
65
66
67 #mysql
68
69 #org.quartz.dataSource.myDS.driver = com.ibm.db2.jcc.DB2Driver
70
71 #org.quartz.dataSource.myDS.URL = jdbc:db2://localhost:50000/db
72
73 #org.quartz.dataSource.myDS.user = db2
74
75 #org.quartz.dataSource.myDS.password = db2
76
77 #org.quartz.dataSource.myDS.maxConnections = 5
78
79
80
81 #oracle
82
83 #org.quartz.dataSource.myDS.driver = oracle.jdbc.driver.OracleDriver
84
85 #org.quartz.dataSource.myDS.URL = jdbc:oracle:thin:@localhost:1521:orcl
86
87 #org.quartz.dataSource.myDS.user = scott
88
89 #org.quartz.dataSource.myDS.password = shao
90
91 #org.quartz.dataSource.myDS.maxConnections = 5
92
93
94
95 #For Tomcat
96
97 org.quartz.jobStore.driverDelegateClass =org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
98
99 #For Weblogic & Websphere
100
101 #org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.WebLogicDelegate
102
103 org.quartz.jobStore.useProperties = false
104
105 org.quartz.jobStore.dataSource = myDS
106
107
108
109
110
111 #JNDI MODE
112
113 #For Tomcat
114
115 org.quartz.dataSource.myDS.jndiURL=java:comp/env/jdbc/oracle
116
117 #For Weblogic & Websphere
118
119 #org.quartz.dataSource.myDS.jndiURL=jdbc/oracle
120
121
122
123
124
125 #============================================================================
126
127 # Configure Plugins
128
129 #============================================================================
130
131
132
133 #org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingJobHistoryPlugin
134
135
136
137 #org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin
138
139 #org.quartz.plugin.jobInitializer.fileNames = jobs.xml
140
141 #org.quartz.plugin.jobInitializer.overWriteExistingJobs = true
142
143 #org.quartz.plugin.jobInitializer.failOnFileNotFound = true
144
145 #org.quartz.plugin.jobInitializer.scanInterval = 10
146
147 #org.quartz.plugin.jobInitializer.wrapInUserTransaction = false
红色加粗部分是集群需要的配置
核心配置解释如下:
org.quartz.jobStore.class 属性为JobStoreTX,
将任务持久化到数据中。因为集群中节点依赖于数据库来传播Scheduler实例的状态,你只能在使用JDBC JobStore 时应用Quartz 集群。
org.quartz.jobStore.isClustered 属性为true,通知Scheduler实例要它参与到一个集群当中。
org.quartz.jobStore.clusterCheckinInterval
属性定义了Scheduler实例检入到数据库中的频率(单位:毫秒)。
Scheduler 检查是否其他的实例到了它们应当检入的时候未检入;
这能指出一个失败的Scheduler 实例,且当前Scheduler 会以此来接管任何执行失败并可恢复的Job。
通过检入操作,Scheduler也会更新自身的状态记录。clusterChedkinInterval越小,Scheduler节点检查失败的Scheduler 实例就越频繁。默认值是 20000 (即20 秒)
3、修改spring-time.xml文件
1 <?xmlversion ="1.0"encoding="UTF-8"?>
2 -//SPRING//DTD BEAN//EN"
3 "http://www.springframework.org/dtd/spring-beans.dtd">
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
增加红色加粗部分代码,注入数据源和加载quartz.properties文件
OK Quartz集群的配置只有这几步,我们来启动项目。。。
我们启着启着….报错了!
17:00:59,718 ERROR ContextLoader:215 - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'startQuertz' defined in class path resource [config/spring/spring-time.xml]: Invocation of init method failed; nested exception is org.quartz.JobPersistenceException:Couldn't store job: Unable to serialize JobDataMap for insertion into database because the value of property 'methodInvoker' is not serializable: org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean [See nested exception: java.io.NotSerializableException: Unable to serialize JobDataMap for insertion into database because the value of property 'methodInvoker' is not serializable: org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean]
我们主要来看红色部分,主要原因就是这个MethodInvokingJobDetailFactoryBean 类中的 methodInvoking 方法,是不支持序列化的,因此在把 quartz 的 task 序列化进入数据库时就会抛这个serializable的错误
4、解决serializable错误解决方案
网上查了一下,解决这个问题,目前主要有两种方案:
4.1.修改Spring的源码
作者重写了MethodInvokingJobDetailFactoryBean
4.2.通过AOP反射对Spring源码进行切面重构
博客地址:http://blog.csdn.net/lifetragedy/article/details/6212831
根据 QuartzJobBean 来重写一个自己的类,然后使用 SPRING 把这个重写的类(我们就名命它为: MyDetailQuartzJobBean )注入 appContext 中后,再使用 AOP 技术反射出原有的 quartzJobx( 就是开发人员原来已经做好的用于执行 QUARTZ 的 JOB 的执行类 ) 。
两种方式我都进行了测试,都可以解决问题,我们这里先通过第二种方式解决这个bug,没有修改任何Spring的源码
4.2.1、增加MyDetailQuartzJobBean.Java
1 package org.leopard.core.quartz;
2
3 import java.lang.reflect.Method;
4
5 import org.apache.commons.logging.Log;
6 import org.apache.commons.logging.LogFactory;
7 import org.quartz.JobExecutionContext;
8 import org.quartz.JobExecutionException;
9 import org.springframework.context.ApplicationContext;
10 import org.springframework.scheduling.quartz.QuartzJobBean;
11
12 /**
13 * 解决Spring和Quartz整合bug
14 *
15 */
16 public class MyDetailQuartzJobBean extends QuartzJobBean {
17 protected final Log logger = LogFactory.getLog(getClass());
18
19 private String targetObject;
20 private String targetMethod;
21 private ApplicationContext ctx;
22
23 protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
24 try {
25
26 logger.info("execute [" + targetObject + "] at once>>>>>>");
27 Object otargetObject = ctx.getBean(targetObject);
28 Method m = null;
29 try {
30 m = otargetObject.getClass().getMethod(targetMethod, new Class[] {});
31
32 m.invoke(otargetObject, new Object[] {});
33 } catch (SecurityException e) {
34 logger.error(e);
35 } catch (NoSuchMethodException e) {
36 logger.error(e);
37 }
38
39 } catch (Exception e) {
40 throw new JobExecutionException(e);
41 }
42
43 }
44
45 public void setApplicationContext(ApplicationContext applicationContext) {
46 this.ctx = applicationContext;
47 }
48
49 public void setTargetObject(String targetObject) {
50 this.targetObject = targetObject;
51 }
52
53 public void setTargetMethod(String targetMethod) {
54 this.targetMethod = targetMethod;
55 }
56
57 }
5、再次修改spring-time.xml文件解决serializable问题
修改后的spring-time.xml文件内容如下:
1 <?xmlversion ="1.0"encoding="UTF-8"?>
2 -//SPRING//DTD BEAN//EN"
3 "http://www.springframework.org/dtd/spring-beans.dtd">
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 org.leopard.core.quartz.MyDetailQuartzJobBean
34
35
36
37
38
39
40
41
42
43
44
45
46
主要看红色加粗部分...
测试
Ok 配置完成,我们把oa_ssh部署到两台tomcat上面,分别启动。
可以看到我们先启动的tomcat控制台打印出日志
另外一台没有打印日志
这时我们把执行定时任务的那台tomcat停止,可以看到等了一会之后,我们的另外一台tomcat会把之前tomcat执行的定时任务接管过来继续执行,我们的集群是成功的。
java集群调度_集群环境下定时调度的解决方案之Quartz集群相关推荐
- 集群环境下定时调度的解决方案之Quartz集群
集群环境下定时调度的解决方案之Quartz集群 参考文章: (1)集群环境下定时调度的解决方案之Quartz集群 (2)https://www.cnblogs.com/yinfengjiujian/p ...
- java 内存映射文件进程间通讯_[转]Windows环境下利用“共享内存”实现进程间通信的C/C++代码---利用CreateFileMapping和MapViewOfFile...
进程间的通信方式有很多种, 上次我们说了最傻瓜的"共享外存/文件"的方法. 那么, 在本文中, 我们即将学习"共享内存"的方式实现进程间的通信, 这是IPC最快 ...
- 绿盟防火墙配置手册_双链路环境下不同品牌防火墙更换案例分享
关注"IT实战基地",与行业大咖交流学习! 引言 不同品牌防火墙在整体更换时,实施迁移时除了将防火墙的接口IP地址,路由,安全策略进行迁移时,在实际更换时还可能遇到一切奇怪的问题, ...
- 检查是否禁止asp.net服务扩展_在 Kubernetes 环境下部署 OpenWhisk 服务
本文使用 Zhihu On VSCode 创作并发布 1. 总体目标 终于开始准备毕业设计了.在和导师们的讨论之后,我们确定之后的研究方向将会专注于 Serverless Computing. 先长舒 ...
- tomcat 请求超时_高并发环境下如何优化Tomcat性能?看完我懂了!
来自:冰河技术 写在前面 Tomcat作为最常用的Java Web服务器,随着并发量越来越高,Tomcat的性能会急剧下降,那有没有什么方法来优化Tomcat在高并发环境下的性能呢? Tomcat运行 ...
- java用户的授权及验证_Java环境下shiro的测试-认证与授权
Java环境下shiro的测试 1.导入依赖的核心jar包 org.apache.shiro shiro-core 1.3.2 2.认证程序 2.1 构建users配置文件 xxx.ini doGet ...
- Java练习-----2.对Windows和Linux环境下输入的文件路径格式进行校验
1.需求 Windows环境下路径格式只能为 D:\Desktop\source Linux环境下路径格式只能为 /data/source 2.结果展示 Linux环境下运行成功,懒得开虚拟机,就不展 ...
- 断网python第三方库安装_断网环境下利用pip安装Python离线安装包(转载)
这几天搞Windows离线断网环境下安装Python包,配置环境,各种坑!做个记录,供以后查询吧. # 生产环境 windows 7 # python 2.7.9 # pip 1.5.2 友情提示:当 ...
- opencv在python环境下的安装_关于python环境下的opencv安装
吐槽: 这一天我终于记起了这个博客.今天搞python环境下的opencv,又弄了一天,很烦躁.之前配置VS的opencv也是花了好久的时间,然后突然发现之前记录在电脑上的文档都找不到了,于是决定还是 ...
最新文章
- vim 删除操作命令
- 2015年第四季度全球闪存行业营收同比减少5%
- 搞笑向, 面向IE8的webworker-ployfill
- C#操作Excel,套用模板并对数据进行分页
- 西门子安装未找到ssf文件_V5.3安装时显示NO SSF FILE FOUND对话框,我该怎么解决啊? 谢谢!-工业支持中心-西门子中国...
- 设计模式(17) 访问者模式(VISITOR) C++实现
- Go语言编程—Go语言实现文件拷贝
- form表单、控制器中接收表单提交数据的4种方式
- 垃圾分类:人机搭配,干活不累
- 汇编语言将十六进制数转换成十进制数_汇编语言--键盘输入两个一位十进制数 ,以十进制数的形式输出其和...
- 解决导入Beautifulsoup 报错 AttributeError: 'module' object has no attribute '_base'的问题
- 永磁同步电机市场现状及未来发展趋势
- Leetcode 318. Maximum Product of Word Lengths
- 对c语言课程的建议和意见,对课程的意见和建议
- Dynamics CRM IFD部署如何更换SSL证书
- 《数据库系统概论(第五版)》学习资料
- java -jar 执行jar包出现 java.lang.NoClassDefFoundError
- 梁宁《产品思维》之27颠覆式创新
- 【matlab】函数meshgrid的用法详解(生成网格矩阵)和ndgrid的区别及用法
- 【本人秃顶程序员】作为字节跳动的研发面试官,有些话我不得不说!
热门文章
- 如何做一个“擅长” Google 搜索的开发专家?
- 腾讯发力研发文化升级,宣布加入Linux基金会TODO 工作组
- 刷屏了!这篇Python学习贴,90%的程序员都用的上!
- 不想打造物联网的制造型企业不是一家合格的百年老店
- 为什么你的 JavaScript 代码如此冗长?!
- JavaScript 详解:为什么写好的代码非常重要
- 那些一心想要离开 BAT 的人,后来怎么样了?
- java rx_史上最浅显易懂的RxJava入门教程
- mysql 时间2039_Apollo使用MySQL时用关键字作为表名报错
- python换零钱有多少种方案_Python之100个项目:零钱转换程序