quartz集群

当项目部署在多个tomcat上进行集群后,定时任务就会有问题. 多个tomcat都会执行相同的定时任务.最简单的解决办法是把定时器只放到一个tomcat上运行,但是这样就不是集群了.

我们期望的是一台服务挂掉,不影响定时器执行. 这时候就用到了quartz的集群.


其实单机运行的时候,使用spring task 最方便, 2行代码就搞定了. 但是spring task不支持集群. 所以集群的话还是用quartz.


1.集群配置

2.测试

3.删除定时任务


1.集群配置

1)数据库脚本

我们可以想象,一个定时任务,只会执行一次,肯定要有一个全局的人来监控到底是哪个quartz去执行了这个任务,然后让别的quartz不再执行这个任务. 

那谁来当这个全局的人呢?  当然是数据库啦. quartz官方也提供了基于内存的Terracotta来管理. 我们这里就用数据库了.



去下载quartz的压缩包,里面自带sql脚本文件.点击打开链接


我们这里用的数据库是 mysql,innodb引擎的. 所以执行标红的sql脚本.


2) 配置文件

首先在spring-quartz中,增加个执行器配置,用于任务注册

	<bean id="executor"
		class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
		<property name="corePoolSize" value="10" />
		<property name="maxPoolSize" value="100" />
		<property name="queueCapacity" value="500" />
	</bean>

然后在总管理类中增加一些属性配置

	<bean id="startQuertz" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
		<property name="triggers">
			<list>
				<ref bean="testTrigger" />
			</list>
		</property>
		<property name="taskExecutor" ref="executor" />
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:quartz.properties" />
	</bean>


其中dataSource 是连接数据库的bean,configLocation是 quartz自身的一些配置

quartz.properties 我们使用官方的 传送门 ,但是把数据库配置的地方改下

#============================================================================
# Configure Main Scheduler Properties  
#============================================================================

org.quartz.scheduler.instanceName = MyClusteredScheduler
org.quartz.scheduler.instanceId = AUTO

#============================================================================
# Configure ThreadPool  
#============================================================================

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 25
org.quartz.threadPool.threadPriority = 5

#============================================================================
# Configure JobStore  
#============================================================================

org.quartz.jobStore.misfireThreshold = 60000

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate 
org.quartz.jobStore.tablePrefix = QRTZ_

org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000

如果这时候你运行项目,会报错

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]


这是因为Quartz集群只支持JDBCJobStore存储方式,而MethodInvokingJobDetailFactoryBean不能序列化存储job数据到数据库.

没办法,只好我们要自己编写个类,继承QuartzJobBean

public class MyDetailQuartzJobBean extends QuartzJobBean {
	 private Logger logger = Logger.getLogger(MyDetailQuartzJobBean.class);
	 
     private String targetObject;
     private String targetMethod;

	@Override
	protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
		try {
			logger.error("开始执行>>>>>> [" + targetObject+":"+ targetMethod + "] ");
			Object otargetObject = SpringContextUtil.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) {
			logger.error(e);
			throw new JobExecutionException(e);
		}
	}

	public void setTargetObject(String targetObject) {
		this.targetObject = targetObject;
	}
	public void setTargetMethod(String targetMethod) {
		this.targetMethod = targetMethod;
	}
}


然后将 MethodInvokingJobDetailFactoryBean 这个类 要换成 JobDetailFactoryBean ,并且引用上面自定义的类,

targetObject和targetMethod 也要换个写法.

整个spring-quartz.xml配置如下

<?xml version="1.0" encoding="UTF-8"?>
<beans default-init-method="init" default-destroy-method="destroy"
	xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans  
  http://www.springframework.org/schema/beans/spring-beans.xsd  ">

	<!-- 要调用的工作类 -->
	<bean id="quartzService" class="com.dingcheng.common.quartz.QuartzService"></bean>
	
	<!-- 定义调用对象和调用对象的方法 -->
	<bean id="testJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
		<property name="jobClass" value = "com.dingcheng.common.quartz.MyDetailQuartzJobBean" />
		<property name="jobDataAsMap"> 
			<map>
				<entry key="targetObject" value="quartzService" />
				<entry key="targetMethod" value="test" />
			</map>
		</property>
	</bean>
	
	<!-- 定义触发时间 -->
	<bean id="testTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
		<property name="jobDetail" ref="testJob" />
		<property name="cronExpression" value="*/20 * * * * ?" />
	</bean>
	
	<!-- 线程执行器配置,用于任务注册 -->
	<bean id="executor"
		class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
		<property name="corePoolSize" value="10" />
		<property name="maxPoolSize" value="100" />
		<property name="queueCapacity" value="500" />
	</bean>
	
	<!-- 总管理类 -->
	<bean id="startQuertz" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
		<property name="triggers">
			<list>
				<ref bean="testTrigger" />
			</list>
		</property>
		<property name="taskExecutor" ref="executor" />
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:quartz.properties" />
	</bean>

</beans>


这样运行项目,就可以了.

2.测试

1)在定时方法中,打印一下路径

	public void test(){
		logger.error("定时器执行了!"+QuartzService.class.getResource("").getFile());
	}


2)把项目放到2个tomcat下,直接启动2个tomcat,2个中有一个会执行,把执行的这个tomcat关掉,另外一个tomcat就自动开始执行.即实现了集群(在linux上面2个tomcat是轮流执行的,windows上是单个执行,挂掉才切换,不知道是偶然还是什么情况).


3)如果你是按照上面的配置文件进行测试, 会发现刚启动的时候,定时器连续执行了好几次.那应该是启动的过程中累计的执行次数一起执行了.为了避免这种情况,需要在总管理类中增加配置

<property name="startupDelay" value="10"/><!-- 延迟加载10秒,即启动后10秒再执行 -->



3.删除任务

因为这个任务是保存到数据库中了, 如果我们想取消某个定时任务怎么办呢?

目前没找到删除的办法,只能从数据库删除.

delete from qrtz_cron_triggers where TRIGGER_NAME = 'test1Trigger' ;
delete from qrtz_triggers where TRIGGER_NAME = 'test1Trigger';
delete from qrtz_job_details where JOB_NAME = 'test1Detail';


网上写的

org.quartz.plugin.jobInitializer.scanInterval = 10  
org.quartz.plugin.jobInitializer.overWriteExistingJobs = true  

貌似都是老皇历了, 现在2.2.x已经找不到scanInterval这个属性了. 等我找到再来更新文章.


删除需要手动,更新还是可以配置的, 例如更新执行时间.

需要在总管理类中增加

<property name="overwriteExistingJobs" value="true" />



源码地址:https://code.csdn.net/qq315737546/ssmq-cluster/tree/master




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值