上篇博客讲了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上多发布几个,来进行测试,这里就不举例子了。