关于quartz框架做集群定时任务调度的总结(注解配置的方式)

接上文,quartz采用2.2.1版本,11张数据库的表格,

1,quartz.properties 配置文件不变(跟上文一样):

#==============================================================      
    #Configure Main Scheduler Properties      
    #==============================================================       
    org.quartz.scheduler.instanceName = mapScheduler    
    org.quartz.scheduler.instanceId = AUTO    
      
    #org.quartz.scheduler.instanceIdGenerator.class  
      
    #==============================================================      
    #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 DataSource      
    #==============================================================     
#    org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver    
#    org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/sdn?useUnicode=true&characterEncoding=UTF-8    
#    org.quartz.dataSource.myDS.user = root    
#    org.quartz.dataSource.myDS.password =     
#    org.quartz.dataSource.myDS.maxConnections = 30    
#    org.quartz.jobStore.selectWithLockSQL = SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE    
        
    #==============================================================      
    #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   

2,application-quartz.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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:task="http://www.springframework.org/schema/task"
	
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
         http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd ">
 
 <!-- 新加的配置 -->
     <bean id="mapScheduler" lazy-init="false" autowire="no"
		class="org.springframework.scheduling.quartz.SchedulerFactoryBean"
		destroy-method="destroy">  
		<!--可选,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="jobFactory">  
            <bean class="com.ky.sdn.fk.server.quartz.SpringQuartzJobFactory"></bean>  
        </property>  
        <property name="dataSource" ref="dataSource"/>  
        
        <!-- 要记得要指定配置文件的位置 -->  
        <property name="configLocation" value="classpath:properties/quartz.properties" />  
    </bean> 
    
</beans>

3,编写一个任务注册调度监听的类,实现ApplicationListener(由springring容器管理):

import java.util.ArrayList;  
import java.util.List;  
import java.util.Map;  
import java.util.Set;  
  
import org.quartz.CronScheduleBuilder;  
import org.quartz.CronTrigger;  
import org.quartz.Job;  
import org.quartz.JobBuilder;  
import org.quartz.JobDetail;  
import org.quartz.JobKey;  
import org.quartz.Scheduler;  
import org.quartz.TriggerBuilder;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.context.ApplicationContext;  
import org.springframework.context.ApplicationListener;  
import org.springframework.context.event.ContextRefreshedEvent;  
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
  
@Component
public class QuartJobSchedulingListener implements ApplicationListener<ContextRefreshedEvent>  
{  
    Logger logger = LoggerFactory.getLogger(QuartJobSchedulingListener.class);  
  
    @Autowired  
    private Scheduler scheduler;  
  
    public void onApplicationEvent(ContextRefreshedEvent event)  
    {  
        try  
        {  
            ApplicationContext applicationContext = event.getApplicationContext();  
            this.loadCronTriggers(applicationContext, scheduler);  
        }  
        catch (Exception e)  
        {  
            logger.error(e.getMessage(), e);  
        }  
    }  
  
    private void loadCronTriggers(ApplicationContext applicationContext, Scheduler scheduler)  
    {  
        Map<String, Object> quartzJobBeans = applicationContext.getBeansWithAnnotation(QuartzJob.class);  
        Set<String> beanNames = quartzJobBeans.keySet();  
        List<CronTrigger> cronTriggerBeans = new ArrayList<CronTrigger>();  
        for (String beanName : beanNames)  
        {  
            Object object = quartzJobBeans.get(beanName);  
            try  
            {  
                if (Job.class.isAssignableFrom(object.getClass()))  
                {  
                    QuartzJob quartzJobAnnotation = AnnotationUtils.findAnnotation(object.getClass(), QuartzJob.class);  
                    JobKey jobKey = new JobKey(quartzJobAnnotation.name(), quartzJobAnnotation.group());  
                    JobDetail job = JobBuilder  
                            .newJob((Class<? extends Job>) object.getClass())  
                            .withIdentity(jobKey)  
                            .build();  
                    CronTrigger cronTrigger = TriggerBuilder  
                            .newTrigger()  
                            .withIdentity(quartzJobAnnotation.name() + "_trigger", quartzJobAnnotation.group())  
                            .withSchedule(CronScheduleBuilder.cronSchedule(quartzJobAnnotation.cronExp()))  
                            .build();  
                    scheduler.scheduleJob(job, cronTrigger);  
                }  
                else  
                {  
                    String errorMsg = object.getClass() + " doesn't implemented " + Job.class.getName();  
                    logger.error(errorMsg);  
                    throw new RuntimeException(errorMsg);  
                }  
            }  
            catch (Exception e)  
            {  
                logger.error(e.getMessage(), e);  
            }  
        }  
    }  
}  

5,编写一个SpringBeanJobFactory 的继承类

import org.quartz.Job;  
import org.quartz.spi.TriggerFiredBundle;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.springframework.beans.BeanWrapper;  
import org.springframework.beans.MutablePropertyValues;  
import org.springframework.beans.PropertyAccessorFactory;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.context.ApplicationContext;  
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import org.springframework.stereotype.Component;
  
public class SpringQuartzJobFactory extends SpringBeanJobFactory  
{  
    Logger logger = LoggerFactory.getLogger(SpringQuartzJobFactory.class);  
      
    @Autowired  
    private ApplicationContext ctx;  
  
    @Override  
    @SuppressWarnings("unchecked")  
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception  
    {  
        Job job = null;  
        try  
        {  
            job = ctx.getBean(bundle.getJobDetail().getJobClass());  
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job);  
            MutablePropertyValues pvs = new MutablePropertyValues();  
            pvs.addPropertyValues(bundle.getJobDetail().getJobDataMap());  
            pvs.addPropertyValues(bundle.getTrigger().getJobDataMap());  
            bw.setPropertyValues(pvs, true);  
        }  
        catch (Exception e)  
        {  
            logger.error(e.getMessage(), e);  
            throw new Exception(e);  
        }  
        return job;  
    }  
} 

6,任务定时策略的注解需要自定义去编写

import java.lang.annotation.Documented;  
import java.lang.annotation.ElementType;  
import java.lang.annotation.Retention;  
import java.lang.annotation.RetentionPolicy;  
import java.lang.annotation.Target;  
  
import org.springframework.stereotype.Component;  
  
@Target({ElementType.TYPE})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Component  
//@Scope("prototype")  
public @interface QuartzJob  
{  
  
    String name();  
  
    String group() default "DEFAULT_GROUP";  
  
    String cronExp();  
} 

7,编写任务类,用注解去实现定时

import java.util.Date;  
  
import org.quartz.JobExecutionContext;  
import org.quartz.JobExecutionException;  
import org.springframework.scheduling.quartz.QuartzJobBean;
  
@QuartzJob(name = "HelloJob", cronExp = "*/10 * * * * ?") 
public class HelloJob extends QuartzJobBean  
{  
	 
    @Override  
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException  
    {  
        System.out.println("Hello Job is running @ " + new Date());  
        System.out.println(this.hashCode());  
    }  
    
}


总结:这种方法的注解是只能一个类写同一种定时策略的n个方法,不能一个类写不同定时策略的n个方法,就算把自定义注解改为方法的注解类型,但是QuartJobSchedulingListener这个类引用了自定义注解类,能力有限,不会改源码了,所以使用具有一点点局限性,一个类只能实现同种策略的方法。

注意:容器首次正常启动后,是不会报异常信息的,再次启动时会报异常,如下:

org.quartz.ObjectAlreadyExistsException: Unable to store Job : 'DEFAULT_GROUP.HelloJob', because one already exists with this identification.
大概意思是表中XXX已经有了数据,不能继续存储进去:

大概是说数据库已经存在了这个HelloJob,也就是容器关闭时没有删除表中信息,配置这个怎么删除我也没搞懂,但是不影响使用

还有一张表需要注意:


这里面有一些cron的表达式,因为重启容器表的数据不会清空,所以改代码的cron表达式的时候记得把job的name也改成表中没有的,这个大概跟triggerName有关系的。(上一篇纯配置实现的时候是不会出现报异常的问题的,大概是已经清空了表,这点待确认一下???)

猜你喜欢

转载自blog.csdn.net/alyone_23/article/details/80674653
今日推荐