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 = MyClusteredSchedulerorg.quartz.scheduler.instanceId = AUTO#============================================================================# Configure ThreadPool  #============================================================================org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPoolorg.quartz.threadPool.threadCount = 25org.quartz.threadPool.threadPriority = 5#============================================================================# Configure JobStore  #============================================================================org.quartz.jobStore.misfireThreshold = 60000org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTXorg.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.tablePrefix = QRTZ_org.quartz.jobStore.isClustered = trueorg.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




           

猜你喜欢

转载自blog.csdn.net/qq_44952610/article/details/89482049