spring 集成quartz 用数据库实现quartz的集群

一:说明
    1. 使用spring 支持quartz 的方式实现定时任务
    2. 使用mysql做quartz的集群
二:准备工作
    1. 安装mqyl(过程略,我的mysql ip为:192.168.75.128), 创建数据库quartz
    2. 下载quartz相关包(http://www.quartz-scheduler.org/downloads/destination?name=quartz-1.8.6.tar.gz&bucket=tcdistributions&file=quartz-1.8.6.tar.gz),
解压后在 docs\dbTables 目录下有相关数据库的sql,用此sql脚本在quartz数据库中执行,创建表/索引(1.8.6和2.x sql脚本不同,这里使用的quartz是1.8.6,因为 spring3.x 不支持quartz 2.x 的版本)
三:demo实现
1. 使用maven管理依赖,依赖如下: 
<properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <spring.version>3.2.3.RELEASE</spring.version>
</properties>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>${spring.version}</version>
 </dependency>
 <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>${spring.version}</version>
 </dependency>
 <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-expression</artifactId>
  <version>${spring.version}</version>
 </dependency>
 <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>${spring.version}</version>
 </dependency>
 <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>${spring.version}</version>
  <scope>compile</scope>
 </dependency>
</dependencies>


2. applicationContext.xml 配置
 <!-- 配置数据源 -->
 <bean id="quartzDataSource" class="org.apache.commons.dbcp.BasicDataSource"  destroy-method="close"> 
  <property name="driverClassName">
   <value>org.gjt.mm.mysql.Driver</value>
  </property>
  <property name="url">
   <value>jdbc:mysql://192.168.75.128:3306/quartz?characterEncoding=utf8</value>
  </property>
  <property name="username">
   <value>root</value>
  </property>
  <property name="password">
   <value>123456</value>
  </property>
  <property name="maxActive" value="5"></property>
  <property name="maxIdle" value="20"></property>
  <property name="maxWait" value="50"></property>
  <property name="defaultAutoCommit" value="true"></property>
 </bean>
 <!-- 配置job -->
 <bean id="jobHelloDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
  <property name="jobClass" value="com.mas.db.JobHello"/> <!-- job实现类 -->
 </bean>
 <!-- 配置trigger -->
 <bean id="jobHelloTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
  <property name="jobDetail" ref="jobHelloDetail" /> <!-- 对应的 job detail-->
  <property name="cronExpression" value="1/2 * * * * ?" /> <!--cronExpression 表达式 -->
 </bean>
 <bean name="quartzScheduler"
  class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  <property name="dataSource">
   <ref bean="quartzDataSource" /> <!--数据源引用指向,包含集群所需的所有表 -->
  </property>
  <!--applicationContextSchedulerContextKey: 是org.springframework.scheduling.quartz.SchedulerFactoryBean这个类中把spring上下
   文以key/value的方式存放在了quartz的上下文中了,可以用applicationContextSchedulerContextKey所定义的key得到对应的spring上下文, 可以看下源码注释-->
  <property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
  <property name="configLocation" value="classpath:quartz.properties" /> <!--用于指明quartz的配置文件的位置 -->
 
  <!-- job trigger 实例加载到 scheduler factory中 -->
  <property name="triggers">
   <list>
    <ref bean="jobHelloTrigger" />
   </list>
  </property>
 </bean>
3. quartz.properties 配置
#属性可为任何值,用在 JDBC JobStore 中来唯一标识实例,但是所有集群节点中必须相同。
org.quartz.scheduler.instanceName = HumsScheduler
#为 AUTO即可,基于主机名和时间戳来产生实例 ID。
org.quartz.scheduler.instanceId = AUTO
orgorg.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
#线程继承初始化线程的上下文类加载器
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
org.quartz.jobStore.misfireThreshold = 60000
#将任务持久化到数据中。因为集群中节点依赖于数据库来传播 Scheduler 实例的状态,你只能在使用 JDBC JobStore 时应用 Quartz 集群。这意味着你必须使用 JobStoreTX 或是 JobStoreCMT 作为 Job 存储;你不能在集群中使用 RAMJobStore。
orgorg.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX 
orgorg.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
#jobStore处理未按时触发的Job的数量
org.quartz.jobStore.maxMisfiresToHandleAtATime=10
#告诉了 Scheduler 实例要它参与到一个集群当中。这一属性会贯穿于调度框架的始终,用于修改集群环境中操作的默认行为。
org.quartz.jobStore.isClustered = true
#定义了Scheduler实例检入到数据库中的频率(单位:毫秒)。Scheduler 检查是否其他的实例到了它们应当检入的时候未检入;这能指出一个失败的 Scheduler 实例,且当前 Scheduler 会以此来接管任何执行失败并可恢复的 Job。通过检入操作,Scheduler 也会更新自身的状态记录。clusterChedkinInterval 越小,Scheduler 节点检查失败的 Scheduler 实例就越频繁。默认值是 15000 (即15 秒)。
org.quartz.jobStore.clusterCheckinInterval = 15000
4. job  com.mas.db.JobHello 的实现(注意job实现类的说明)
	/**
	 * 1. 继承 QuartzJobBean
	 * 2. 如果任务处理时间大于job触发频率时间,那么任务会并行处理,
	 * 实现 StatefulJob后job就会在一次任务没有处理完就不会进行下一次任务(任务的串行处理)
	 *
	 */
	public class JobHello extends QuartzJobBean implements StatefulJob {
		@Override
		protected void executeInternal(JobExecutionContext arg0)
				throws JobExecutionException {
			try {
				//任务处理
				System.out.println("job hello"+System.currentTimeMillis());
				//模拟任务处理时间大于job触发频率
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
//任务处理 System.out.println("job hello"+System.currentTimeMillis()); //模拟任务处理时间大于job触发频率 Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } }}
 5. 测试1. 可以起多个main方法进行测试(HelloTest 和 HelloTest2),多个main方法同时起动后只有一个job在执行,当停止一个main线程后,等到Scheduler实例检入到数据库中的频率(quartz.properties 是配置的org.quartz.jobStore.clusterCheckinInterval ) 过后,就会有一个线程接着执行这个job
 
public class HelloTest {
    public static void main(String[] args) {
        //定时任务开启模拟
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    }
}
2.UpdateQuartzTest 测试类中是对 trigger的操作测试,如:修改触发频率、暂停、重启,获取trigger的运行状态,获取全部的trigger。如需更多对trigger或者job的操作可查看org.quartz.impl.StdScheduler 源码。
注意:修改trigger 的触发频率后,再暂停,然后重新启动;发现trigger是触发频率和初始化的时候相同, 说明:修改trigger触发频率只是对运行中的trigger有效,如果trigger重新启动则trigger触发频率还原为配置的频率

四:demo源码,在test中有两个测试类( HelloTest 和 HelloTest2  模拟部属两个集群。 UpdateQuartzTest  中为操作trigger是测试,测试时需要启动HelloTest或者HelloTest2,两者都启动亦可 ).
完整代码:http://download.csdn.net/detail/convict_eva/9626160

六:Terracotta公司有个脱离关系型数据库实现集群解决方案的方法   http://www.terracotta.org/products/quartz-scheduler

备注:定时器时间配置
1.秒(0–59)
2.分钟(0–59)
3.小时(0–23)
4.月份中的日期(1–31)
5.月份(1–12或JAN–DEC)
6.星期中的日期(1–7或SUN–SAT)
7.年份(1970–2099)

示例:
"0/10 * * * * ?" 每10秒触发 
"0 0 12 * * ?" 每天中午12点触发 
"0 15 10 ? * *" 每天上午10:15触发 
"0 15 10 * * ?" 每天上午10:15触发 
"0 15 10 * * ? *" 每天上午10:15触发 
"0 15 10 * * ? 2005" 2005年的每天上午10:15触发 
"0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发 
"0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发 
"0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 
"0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发 
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发 
"0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发 
"0 15 10 15 * ?" 每月15日上午10:15触发 
"0 15 10 L * ?" 每月最后一日的上午10:15触发 
"0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发 
"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发 
"0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发 

说明:
星号(*):可用在所有字段中,表示对应时间域的每一个时刻,例如,*在分钟字段时,表示“每分钟”;
问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符;
减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12;
逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;
斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在分钟字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y;
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页