Fork me on GitHub

分布式任务调度——quartz + spring + 数据库

    项目中使用分布式并发部署定时任务,多台跨JVM,按照常理逻辑每个JVM的定时任务会各自运行,这样就会存在问题,多台分布式JVM机器的应用服务同时干活,一个是加重服务负担,另外一个是存在严重的逻辑问题,比如需要回滚的数据,就回滚了多次,刚好quartz提供很好的解决方案。
    集群分布式并发环境中使用QUARTZ定时任务调度,会在各个节点会上报任务,存到数据库中,执行时会从数据库中取出触发器来执行,如果触发器的名称和执行时间相同,则只有一个节点去执行此任务。 
    如果此节点执行失败,则此任务则会被分派到另一节点执行,中途也会自动检查失效的定时调度,发现不成功的,其他节点立马接过来继续完成定时任务。对应的定时任务调度表比较多,有11个。
 
后续功能
    准备code一个web调度工作台,对所有job进行统计管理和监控;
 
引入依赖:
<!-- quartz -->
<dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
        <version>2.2.1</version>
</dependency>
 
配置quartz数据库
    官网下载,tz压缩包,在解压目录docs\dbTables下找到对应的执行sql,执行即可;
 
配置本地quartz.properties
    单机版,使用RAMJobStore存储任务,可以使用jar包总默认配置,改方式执行最迅速,强大,方便;
    集群版本,需要copy出jar包中配置,新增如下配置,并注释掉重复配置项目,即可;
        # for cluster
        org.quartz.scheduler.instanceId: AUTO
        org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
        org.quartz.jobStore.isClustered: true
        org.quartz.jobStore.clusterCheckinInterval: 1000
 
创建CronTrigger和JobDetail,二者为包含关系
Quartz2.0创建JobDetail有两种方式:
方式一:CronTrigger包含JobDetail,JobDetail用MethodInvokingJobDetailFactoryBean工厂Bean包装普通的Java对象或bean;
<!-- Job trigger -->
<bean id="job01Trigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
      <property name="jobDetail" >
           <bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
                <property name="targetObject" ref="jobService" />
                <property name="targetMethod" value="job01" />
                <property name="concurrent" value="false" />
           </bean>
      </property>
      <property name="cronExpression" value="0/3 * * * * ? *" />
 </bean>
方式二:CronTrigger包含JobDetail,JobDetail用JobDetailFactoryBean包装QuartzJobBean的继承子类(即Job类)的实例;
    <!-- Job trigger -->
    <bean id="job02Trigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" >
            <bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
                <property name="jobClass" value="com.xxl.job.JobDetailDemo"/>
                <property name="jobDataAsMap">
                    <map>
                        <!-- <entry key="xxService" value-ref="xxService" /> -->
                    </map>
                </property>  
                <property name="durability" value="true"></property>
            </bean>
        </property>
        <property name="cronExpression" value="0/1 * * * * ? *" />
    </bean>
 
创建调度工厂,根据数据存储方式,分为两种
方式一:内存RAMJobStore:把job的相关信息存储在内存里,如果用spring配置quartz的job信息的话,所有信息是配置在xml里,当spirng context启动的时候就把xml里的job信息装入内存。
<bean name="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
      <property name="triggers">
           <list>
                <ref bean="job01Trigger" />
           </list>
      </property>
 </bean>
方式二:数据库:读取配置在数据库里的job初始化信息,并且把job通过java序列化到数据库里,这样就使得每个job信息得到了持久化,即使在jvm或者容器挂掉的情况下,也能通过数据库感知到其他job的状态和信息;quartz集群各节点之间是通过同一个数据库实例(准确的说是同一个数据库实例的同一套表)来感知彼此的。 
<bean id="quartzScheduler" lazy-init="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> 
      <property name="dataSource" ref="dataSource" />
      <property name="autoStartup" value="true" />
      <property name="applicationContextSchedulerContextKey"  value="applicationContextKey" />
       <property name="configLocation" value="classpath:quartz.properties"/>
      <property name="triggers">
           <list>
                <ref bean="job02Trigger" />
           </list>
      </property>
 </bean>
 
tips:applicationContextSchedulerContextKey: 是org.springframework.scheduling.quartz.SchedulerFactoryBean这个类中把spring上下 文以key/value的方式存放在了quartz的上下文中了,可以用applicationContextSchedulerContextKey所定义的key得到对应的spring上下文。
 
实例应用
..quartz-demo.zip..
 
 
 
 
 
posted @ 2015-10-10 12:06  许雪里  阅读(13137)  评论(6编辑  收藏  举报