1. 程式人生 > >Spring boot + Quartz實現分散式定時任務

Spring boot + Quartz實現分散式定時任務

在實際專案中經常會用到定時任務,且有些定時任務同時只能執行一個例項,下面介紹一下通過Spring boot + Quartz框架實現分散式定時任務。
1. 定時任務持久化到Mysql
2. 名稱為JobA的定時任務每10秒執行一次@ScheduledJob(name = “JobA”, cronExp = “/10 * * * ?”)
3. @DisallowConcurrentExecution同一個定時任務同一時間只能有一個執行,如果定時任務執行時間超過10秒,丟棄或等待(未驗證)
4. 此定時任務可以同時部署多個,支援Failover
5. Quartz相關SQL請參考https://github.com/465919283/demo/blob/master/sql/quartz.sql

quartz.properties

org.quartz.scheduler.instanceName=spring-boot-quartz
org.quartz.scheduler.instanceId=AUTO
org.quartz.threadPool.threadCount=5
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz
.jobStore.useProperties=false org.quartz.jobStore.tablePrefix=QRTZ_ org.quartz.jobStore.isClustered=true
import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import
org.springframework.beans.factory.config.PropertiesFactoryBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import org.springframework.scheduling.quartz.SpringBeanJobFactory; import org.springframework.transaction.PlatformTransactionManager; @Configuration public class QuartzConfig { public static final String QUARTZ_PROPERTIES_PATH = "quartz.properties"; @Autowired private DataSource dataSource; @Autowired private PlatformTransactionManager transactionManager; @Bean public JobFactory jobFactory(ApplicationContext applicationContext) { AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(); jobFactory.setApplicationContext(applicationContext); return jobFactory; } @Bean public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) throws IOException { SchedulerFactoryBean factory = new SchedulerFactoryBean(); factory.setAutoStartup(true); factory.setJobFactory(jobFactory); factory.setQuartzProperties(quartzProperties()); //使用Spring boot中配置的資料來源 factory.setDataSource(dataSource); factory.setTransactionManager(transactionManager); return factory; } @Bean public Properties quartzProperties() throws IOException { PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); propertiesFactoryBean.setLocation(new ClassPathResource(QUARTZ_PROPERTIES_PATH)); propertiesFactoryBean.afterPropertiesSet(); return propertiesFactoryBean.getObject(); } public static class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware { private transient AutowireCapableBeanFactory beanFactory; @Override public void setApplicationContext(final ApplicationContext context) { beanFactory = context.getAutowireCapableBeanFactory(); } @Override protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { final Object job = super.createJobInstance(bundle); beanFactory.autowireBean(job); return job; } } }
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ScheduledJob {

    String name();

    String group() default "DEFAULT_GROUP";

    String cronExp();
}
import org.quartz.CronScheduleBuilder;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

@Component
public class ScheduleListener implements BeanPostProcessor {

    @Autowired
    private Scheduler scheduler;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        ScheduledJob scheduledJob = AnnotationUtils.findAnnotation(bean.getClass(), ScheduledJob.class);
        if (scheduledJob != null && bean instanceof Job) {
            JobKey jobKey = new JobKey(scheduledJob.name(), scheduledJob.group());

            JobDetail jobDetail = JobBuilder.newJob(((Job) bean).getClass())
                    .withIdentity(jobKey)
                    .build();

            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(scheduledJob.name() + "Trigger", scheduledJob.group())
                    .forJob(jobDetail)
                    .withSchedule(CronScheduleBuilder.cronSchedule(scheduledJob.cronExp()))
                    .build();

            try {
                if (!scheduler.checkExists(jobKey)) {
                    scheduler.scheduleJob(jobDetail, trigger);
                }
            } catch (SchedulerException e) {
                e.printStackTrace();
            }
        }
        return bean;
    }
}
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;

import com.primer.demo.task.ScheduledJob;

@Component
@ScheduledJob(name = "JobA", cronExp = "*/10 * * * * ?")
@DisallowConcurrentExecution
public class JobA implements Job {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // TODO Auto-generated method stub
        System.out.println("JobA................");
    }
}