• Spring Quartz分布式定时任务框架搭建


    代码版本

    使用的版本是2.3.2,无漏洞,是最新的发行版本。

    参考

    Quartz官网

    2.3.2源码github

    官方指导翻译参考 --注意该指导不是最新的版本

    基于spring+quartz的分布式定时任务框架

    建表

    建表脚本在源码包的位置

    由于使用的是分布式方式,需要建表。使用MySql的InnoDb的引擎建表。脚本在源码包的。

    quartz-2.3.2quartz-coresrcmain esourcesorgquartzimpljdbcjobstore

    Quartz11张表的解释

    参考

    参考2

    序号 表名 说明 备注
    1 QRTZ_JOB_DETAILS 存放JobDetail的具体信息。核心:JobName,JobGroup,描述,ClassName与是否独立存储等
    2 QRTZ_TRIGGERS 存储所有定义的Trigger信息,包括名称、分组、类型(CRON或SIMPLE)、当前运行状态、上下次执行时间、MisFire策略。和qrtz_fired_triggers存放的不一样,不管trigger触发了多少次都只有一条记录,TRIGGER_STATE用来标识当前trigger的状态
    3 QRTZ_SIMPLE_TRIGGERS 存储SIMPLE类型的Trigger(SimpleTrigger),存储名称、分组、重复次数、执行时间间隔已经已经执行的次数。失火策略该表不存,存放于ORTZ_TRIGGER表中。 不使用SimpleTrigger,不需要关注
    4 QRTZ_CRON_TRIGGERS 存储CRON类型的Trigger(CronTrigger),最常用的触发器。包括名称、所属组、Cron表达式、时区信息 重点,需要使用CronTrigger
    5 QRTZ_SIMPROP_TRIGGERS 存储CalendarIntervalTrigger和DailyTimeIntervalTrigger两种类型的触发器 不使用,可暂不关注
    6 QRTZ_BLOB_TRIGGERS 自定义的triggers使用blog类型进行存储,非自定义的triggers不会存放在此表中,Quartz提供的triggers包括:CronTrigger,CalendarIntervalTrigger, DailyTimeIntervalTrigger以及SimpleTrigger,这几个trigger信息会保存在其他的几张表中 不需要自定义trigger,可暂不关注
    7 QRTZ_CALENDARS Quartz为我们提供了日历的功能,可以自己定义一个时间段,可以控制触发器在这个时间段内触发或者不触发;现在提供6种类型:AnnualCalendar,CronCalendar,DailyCalendar,HolidayCalendar,MonthlyCalendar,WeeklyCalendar 暂不使用,使用cron表达式目前已经满足需要
    8 QRTZ_PAUSED_TRIGGER_GRPS 存储暂停的Trigger组 重要性低,可暂不关注
    9 QRTZ_FIRED_TRIGGERS 存储正在执行任务中的Trgigger,trigger随着时间的推移状态发生变化,直到最后trigger执行完成,从表中被删除。主要存储运行服务器节点ID、Trigger名称分组、触发器执行状态、触发与调度时间。相同的trigger和task,每触发一次都会创建一个实例;从刚被创建的ACQUIRED状态,到EXECUTING状态,最后执行完从数据库中删除;
    10 QRTZ_SCHEDULER_STATE 存储所有节点的scheduler,会定期检查scheduler是否失效。上次检查时间与检测状态。
    11 QRTZ_LOCKS Quartz提供的锁表,为多个节点调度提供分布式锁,实现分布式调度

    脚手架搭建

    数据库初始化

    在数据库环境上新建数据库common,执行quartz的初始化脚本tables_mysql_innodb.sql

    使用Spring提供的Quartz支持

    <dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.2</version>
    </dependency>
    <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context-support</artifactId>
       <version>5.2.5.RELEASE</version>
     </dependency>
    
    

    核心类

    参考如下,可以整改为基于注解的方式

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:task="http://www.springframework.org/schema/task"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
            http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
    
        <bean name="secondCron" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
            <property name="jobClass" value="com.hafiz.www.cron.SecondCron"/>
            <property name="jobDataAsMap">
                <map>
                    <entry key="timeout" value="0"/>
                </map>
            </property>
        </bean>
    
        <!--Cron表达式触发器-->
        <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
            <property name="jobDetail" ref="secondCron"/>
            <property name="cronExpression" value="0/5 * * * * ?"/>
        </bean>
    
        <!--配置调度工厂-->
        <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
            <property name="triggers">
                <list>
                    <!--<ref bean="simpleTrigger"/>-->
                    <ref bean="cronTrigger"/>
                </list>
            </property>
        </bean>
    </beans>
    
    

    基本关系是,SchedulerFactoryBean通过触发器JobDetailFactoryBean执行包含任务类SecondCron的JobDetailFactoryBean。其中SecondCron是业务实现。

    Spring与Quartz的整合

    参考下面的链接初始化表信息及配置。

    参考

    可以将集成修改为注入形式,注入方式配置参考下面的链接:

    参考

    Spring 整合 Quartz 分布式调度

    参考

    job中无法注入service解决

    job实例在spring容器加载时候,能够注入bean,但是调度时,job对象会重新创建,此时就是导致已经注入的对象丢失,因此报空指针异常。通过重写JobFactory解决该问题

    代码

    1、配置文件database.properties、quartz.properties、cron.properties

    通过database.properties初始化DataSource数据源(可以是Druid等任意实现了DataSource的框架)

    @Configuration
    @PropertySource("classpath:database.properties")
    public class DataSourceConfiguration {
        @Value("${servers}")
        private String servers;
    
        @Value("${userName}")
        private String userName;
    
        @Value("${encPasswd}")
        private String encPasswd;
    
        @Bean
        public DataSource dataSource() {
    
            DataSource dataSource = new DataSource();
            dataSource.setServers(servers);
            dataSource.setUserName(userName);
            dataSource.setEncPasswd(encPasswd);
            
            return dataSource;
        }
    }
    

    quartz.properties

    #============================================================================
    # Configure Main Scheduler Properties
    #============================================================================
    
    org.quartz.scheduler.instanceName = petal-quartz
    org.quartz.scheduler.instanceId = AUTO
    org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer = true
    
    #============================================================================
    # Configure ThreadPool
    #============================================================================
    
    org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
    org.quartz.threadPool.threadCount = 50
    
    #============================================================================
    # Configure JobStore
    #============================================================================
    
    org.quartz.jobStore.misfireThreshold = 60000
    
    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.useProperties = true
    org.quartz.jobStore.isClustered = true
    org.quartz.jobStore.clusterCheckinInterval = 20000
    
    org.quartz.scheduler.wrapJobExecutionInUserTranscation=false
    
    

    cron.properties cron表达式配置文件,后期可以整改为数据库管理

    cron.demo=0 * * * * ?
    
    2、使用注解方式配置
    //SchedulerConfig
    package com.xxx.config.quartz;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.Properties;
    
    import javax.sql.DataSource;
    
    import org.quartz.Scheduler;
    import org.quartz.Trigger;
    import org.quartz.spi.JobFactory;
    import org.springframework.beans.BeansException;
    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.CronTriggerFactoryBean;
    import org.springframework.scheduling.quartz.SchedulerFactoryBean;
    
    /**
     * Spring Quartz配置
     *
     * @since 2021-01-16
     */
    @Configuration
    public class SchedulerConfig implements ApplicationContextAware {
    
        private ApplicationContext ctx;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.ctx = applicationContext;
        }
    
        /**
         * 目的是让所有的Job都能被 Autorwired
         * @param context
         * @return job factory
         */
        @Bean
        public JobFactory jobFactory(ApplicationContext context) {
            AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
            jobFactory.setApplicationContext(context);
            return jobFactory;
        }
    
        /**
         * SchedulerFactoryBean 配置类
         *
         * @since 8:58 2021/1/18
         * @param dataSource 数据源
         * @param jobFactory 此处的jobFactory已被重写,使用的是AutowiringSpringBeanJobFactory#createJobInstance 创建job
         * @return org.springframework.scheduling.quartz.SchedulerFactoryBean
         */
        @Bean
        public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource, JobFactory jobFactory) throws Exception {
    
            SchedulerFactoryBean factory = new SchedulerFactoryBean();
    
            factory.setDataSource(dataSource);
            factory.setOverwriteExistingJobs(true);
            factory.setJobFactory(jobFactory);
    
            factory.setStartupDelay(60);
    
            factory.setQuartzProperties(quartzProperties());
            factory.afterPropertiesSet();
    
            // 将所有CronTriggerFactoryBean类型的触发器都注册到SchedulerFactory
            Map<String, CronTriggerFactoryBean> triggerFactoryBeansMap =
                this.ctx.getBeansOfType(CronTriggerFactoryBean.class);
            List<Trigger> triggerBeansList = new ArrayList<>();
            triggerFactoryBeansMap.forEach((k, v) -> {
                triggerBeansList.add(v.getObject());
            });
            factory.setTriggers(triggerBeansList.toArray(new Trigger[triggerBeansList.size()]));
            return factory;
        }
    
        /**
         * quartz的自定义配置
         *
         * @since 15:53 2021/1/16
         * @param
         * @return java.util.Properties
         */
        @Bean
        public Properties quartzProperties() throws IOException {
            PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
            propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
            propertiesFactoryBean.afterPropertiesSet();
            return propertiesFactoryBean.getObject();
        }
    
        /**
         *
         * @param schedulerFactoryBean
         * @return scheduler, 通过定时任务配置,随时rescheduler
         */
    
        @Bean
        public Scheduler scheduler(SchedulerFactoryBean schedulerFactoryBean) {
            return schedulerFactoryBean.getScheduler();
        }
    }
    
    //AutowiringSpringBeanJobFactory 解决job无法注入service问题
    
    package com.xxx.config.quartz;
    
    import org.quartz.spi.TriggerFiredBundle;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.scheduling.quartz.SpringBeanJobFactory;
    
    /**
     * job实例在spring容器加载时候,能够注入bean,但是调度时,job对象会重新创建,此时就是导致已经注入的对象丢失,因此报空指针异常。
     * 通过重写JobFactory解决该问题
     *
     * @since 8:56 2021/1/18
     */
    public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
    
        private transient AutowireCapableBeanFactory beanFactory;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            beanFactory = applicationContext.getAutowireCapableBeanFactory();
        }
    
        /**
         * 这里覆盖了super的createJobInstance方法,对其创建出来的类再进行autowire。
         *
         * @param bundle
         * @return
         * @throws Exception
         */
        @Override
        protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
            final Object jobInstance = super.createJobInstance(bundle);
            beanFactory.autowireBean(jobInstance);
            return jobInstance;
        }
    }
    
    
    
    
    3、配置JobDetailFactoryBean和CronTriggerFactoryBean
    package com.xxx.quartztask;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
    import org.springframework.scheduling.quartz.JobDetailFactoryBean;
    
    import com.huawei.hms.mail.constant.JobName;
    import com.huawei.hms.mail.quartzjob.QuartzDemoJob;
    
    /**
     * Quartz 样例,构建CronTriggerFactoryBean
     *
     * @since 16:20 2021/1/16
     */
    @Configuration
    @PropertySource("classpath:cron.properties")
    public class QuartzDemoTask {
    
        @Value("${cron.demo}")
        private String cron;
    
        @Bean(name = "quartzDemoJob")
        public JobDetailFactoryBean jobDetailFactoryBean() {
            JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
            factoryBean.setDurability(true);
            factoryBean.setJobClass(QuartzDemoJob.class);
            factoryBean.setName(JobName.DEMO_JOB);
            return factoryBean;
        }
    
        @Bean(name = "quartzDemoCronTrigger")
        public CronTriggerFactoryBean cronTriggerFactoryBean() {
            CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
            factoryBean.setJobDetail(jobDetailFactoryBean().getObject());
            factoryBean.setStartDelay(10);
            factoryBean.setCronExpression(cron);
            return factoryBean;
        }
    }
    

    4、Job类

    @Slf4j
    @Component
    public class QuartzDemoJob implements Job {
    
        @Autowired
        private QuartsDemoService quartsDemoService;
    
        @Override
        public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            quartsDemoService.sayHello();
        }
    }
    

    5、Service业务实现类

    /**
     * Quartz 样例 定时任务 业务逻辑
     *
     * @since 2021-01-16
     */
    @Service
    public class QuartsDemoService {
    
        public void sayHello() {
            System.out.println("hello world");
        }
    }
    
    

    添加定时任务的步骤

    1、新建Task类,构建JobDetailFactoryBean和CronTriggerFactoryBean并分别使用names属性唯一标识

    2、在cron.properties中配置唯一命名的表达式,如cron.demo= xxx

    3、新建Job类,需要实现Job接口,并重写execute方法,一般直接调用service实现

    4、新建Servcie业务实现类,给Job类调用

    5、声明job唯一名称JobName.DEMO_JOB常量

    本博客用于技术学习,所有资源都来源于网络,部分是转发,部分是个人总结。欢迎共同学习和转载,转载请在醒目位置标明原文。如有侵权,请留言告知,及时撤除。
  • 相关阅读:
    Python神库分享之geoip2 IP定位库
    科普一下推荐引擎
    浏览器插件之王-Tampermonkey(油猴脚本)
    Swagger入门教程
    使用SonarQube+Eclipse来分析python代码
    什么是搜索引擎蜘蛛?
    让所有网站都提供API的Python库:Toapi
    如何提高自己的逻辑思维能力?
    推荐系统和搜索引擎的关系
    html 后台页面布局
  • 原文地址:https://www.cnblogs.com/z00377750/p/14291625.html
Copyright © 2020-2023  润新知