上篇博客讲了quartz的用法,解决了定时任务的问题,但是当我们搭建集群,将服务部署在多个机器上时,很有可能引起冲突的问题,一个任务,多次执行,那么如何解决这个问题呢,quartz提供了集群的搭建方案,确保一个任务,只会在一个时间执行一次,下面我们来学习一下quartz集群的搭建。
原理
quartz连接数据库,然后从表里去读取相关配置信息,多个任务通过表来达到统一。
搭建
环境
spring 4.0.6
quartz 2.2.3
mysql 5.1.38
druid 1.1.0
首先来看一下目录结构:
pom文件引用如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.spring.quartzm</groupId>
<artifactId>quartzm</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<springframework.version>4.0.6.RELEASE</springframework.version>
<quartz.version>2.2.3</quartz.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${springframework.version}</version>
</dependency>
<!-- spring-tx包必须导入,因为Quartz需要依赖该包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springframework.version}</version>
</dependency>
<!-- Quartz framework -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>${quartz.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.0</version>
</dependency>
</dependencies>
</project>
数据库连接
数据库的建表语句等在quartz-2.2.3-distribution.tar.gz包里面quartz-2.2.3-distribution.tar\quartz-2.2.3\docs\dbTables\下,因为我的数据库是mysql,所以我选择的是tables_mysql_innodb.sql
spring我只用了一个配置文件,名字为quartz-context.xml,数据库建好以后,配置数据库连接,如下:
<!--数据库连接设置-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="${itoo_jdbc_url}"/>
<property name="username" value="${itoo_jdbc_username}"/>
<property name="password" value="${itoo_jdbc_password}"/>
</bean>
<!--<import resource="classpath:quartz-db.properties"/>-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="1" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="locations">
<list>
<value>classpath:db.properties</value>
<!--<value>classpath:dubbo-server.properties</value>-->
</list>
</property>
</bean>
quartz配置文件
配置文件内容为:
#==============================================================
#Configure Main Scheduler Properties
#==============================================================
org.quartz.scheduler.instanceName = defaultScheduler
org.quartz.scheduler.instanceId = AUTO
#==============================================================
#Configure JobStore
#==============================================================
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
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.maxMisfiresToHandleAtATime = 1
org.quartz.jobStore.misfireThreshold = 120000
org.quartz.jobStore.txIsolationLevelSerializable = true
#==============================================================
#Configure ThreadPool
#==============================================================
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
#==============================================================
#Skip Check Update
#update:true
#not update:false
#==============================================================
org.quartz.scheduler.skipUpdateCheck = true
#============================================================================
# Configure Plugins
#============================================================================
org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingJobHistoryPlugin
org.quartz.plugin.shutdownhook.class = org.quartz.plugins.management.ShutdownHookPlugin
org.quartz.plugin.shutdownhook.cleanShutdown = true
spring配置文件内容
JobDetail配置
上篇博客中我们配置jobDetail时使用的是org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean
但是在搭建集群时使用这个会报错,说我们的任务类没有序列化,所以我们使用另一种
org.springframework.scheduling.quartz.JobDetailFactoryBean
如下:
<bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.cdsmartlink.service.impl.ExtendsJob"/>
<property name="durability" value="true"/>
<property name="requestsRecovery" value="true"/>
</bean>
Trigger与上一篇相同即可
<!-- 创建SimpleTrigger触发器 -->
<bean class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean" id="simpleTrigger">
<!-- 引用任务 -->
<property name="jobDetail" ref="jobDetail"/>
<!-- 指定循环时间,以秒为单位 -->
<property name="repeatInterval" value="10000"/>
</bean>
<!-- 创建CronTrigger触发器 -->
<bean class="org.springframework.scheduling.quartz.CronTriggerFactoryBean" id="cronTrigger">
<!-- 引用任务 -->
<property name="jobDetail" ref="jobDetail"/>
<!-- 指定Cron表达式 -->
<property name="cronExpression" value="*/5 * * * * ?"/>
</bean>
Scheduling需要注意一下
<!-- 创建调度器 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean" id="stdScheduler">
<property name="dataSource" ref="dataSource"></property>
<!--可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 -->
<property name="overwriteExistingJobs" value="true" />
<!--必须的,QuartzScheduler 延时启动,应用启动完后 QuartzScheduler 再启动 -->
<property name="startupDelay" value="30" />
<!-- 设置自动启动 -->
<property name="autoStartup" value="true" />
<property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
<property name="configLocation" value="classpath:quartz.properties" />
<property name="triggers">
<list>
<ref bean="cronTrigger"/>
</list>
</property>
</bean>
完整的quartz-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.cdsmartlink.*" />
<!--<!– 创建任务 –>-->
<!--<bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean" id="jobDetail">-->
<!--<!– 目标对象 –>-->
<!--<property name="targetObject" ref="myJob"/>-->
<!--<!– 目标方法 –>-->
<!--<property name="targetMethod" value="execute"/>-->
<!--<!–concurrent:false表示上一个任务执行完后再开启新的任务–>-->
<!--<property name="concurrent" value="false"/>-->
<!--</bean>-->
<bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.cdsmartlink.service.impl.ExtendsJob"/>
<property name="durability" value="true"/>
<property name="requestsRecovery" value="true"/>
</bean>
<!-- 创建SimpleTrigger触发器 -->
<bean class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean" id="simpleTrigger">
<!-- 引用任务 -->
<property name="jobDetail" ref="jobDetail"/>
<!-- 指定循环时间,以秒为单位 -->
<property name="repeatInterval" value="10000"/>
</bean>
<!-- 创建CronTrigger触发器 -->
<bean class="org.springframework.scheduling.quartz.CronTriggerFactoryBean" id="cronTrigger">
<!-- 引用任务 -->
<property name="jobDetail" ref="jobDetail"/>
<!-- 指定Cron表达式 -->
<property name="cronExpression" value="*/5 * * * * ?"/>
</bean>
<!-- 创建调度器 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean" id="stdScheduler">
<property name="dataSource" ref="dataSource"></property>
<!--可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 -->
<property name="overwriteExistingJobs" value="true" />
<!--必须的,QuartzScheduler 延时启动,应用启动完后 QuartzScheduler 再启动 -->
<property name="startupDelay" value="30" />
<!-- 设置自动启动 -->
<property name="autoStartup" value="true" />
<property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
<property name="configLocation" value="classpath:quartz.properties" />
<property name="triggers">
<list>
<ref bean="cronTrigger"/>
</list>
</property>
</bean>
<!--数据库连接设置-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="${itoo_jdbc_url}"/>
<property name="username" value="${itoo_jdbc_username}"/>
<property name="password" value="${itoo_jdbc_password}"/>
</bean>
<!--<import resource="classpath:quartz-db.properties"/>-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="1" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="locations">
<list>
<value>classpath:db.properties</value>
<!--<value>classpath:dubbo-server.properties</value>-->
</list>
</property>
</bean>
</beans>
任务类
任务类需要实现Job接口
package com.cdsmartlink.service;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;
/**
* Created by L on 2017-07-22.
*/
@Component
public class ExtendsJob implements Job{
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("这里是我们的任务代码!!!!");
}
}
测试
package com.cdsmartlink.service.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Created by L on 2017-07-22.
*/
public class MyJobTest{
@Test
public void test() throws InterruptedException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("quartz-context.xml");
Thread.sleep(60000);
}
}
其实也可以在tomcat上多发布几个,来进行测试,这里就不举例子了。
总结
一开始研究Quartz集群的时候,其实我是很无语的,毕竟感觉这个用处不是很大,不过研究着就会发现,有很多比自己牛多了的人,在全心全意为人民服务。