zoukankan      html  css  js  c++  java
  • SpringBoot整合任务调度框架Quartz及持久化配置

    本文侧重SpringBoot与Quartz的整合,Quartz的基本入门概念不清楚的小伙伴可以看看这篇文章:任务调度框架Quartz快速入门!

    本篇要点

    • 介绍SpringBoot与Quartz单机版整合。
    • 介绍Quartz持久化存储。

    SpringBoot与Quartz单机版快速整合

    学习完非Spring环境下Quartz的使用,再来看SpringBoot你会感到更加自如,因为SpringBoot无非是利用它自动配置的特性,将一些重要的Bean自动配置到环境中,我们直接开箱即用,关于Quartz的自动配置定义在QuartzAutoConfiguration中。

    引入依赖

    主要是spring-boot-starter-quartz这个依赖,是SpringBoot与Quartz的整合。

            <!-- 实现对 Spring MVC 的自动化配置 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <!-- 实现对 Quartz 的自动化配置 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-quartz</artifactId>
            </dependency>
    

    创建Job

    为了演示两种Trigger及两种配置方式,我们创建两个不同的Job。

    @Slf4j
    public class FirstJob extends QuartzJobBean {
    
        @Override
        protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            String now = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now());
            log.info("当前的时间: " + now);
        }
    }
    
    @Slf4j
    public class SecondJob extends QuartzJobBean {
    
        @Override
        protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            String now = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now());
            log.info("SecondJob执行, 当前的时间: " + now);
        }
    }
    

    我们在创建Job的时候,可以实现Job接口,也可以继承QuartzJobBean。

    QuartzJobBean实现了Job,并且定义了公用的execute方法,子类可以继承QuartzJobBean并实现executeInternal方法。

    public abstract class QuartzJobBean implements Job {
    
    	/**
    	 * This implementation applies the passed-in job data map as bean property
    	 * values, and delegates to {@code executeInternal} afterwards.
    	 * @see #executeInternal
    	 */
    	@Override
    	public final void execute(JobExecutionContext context) throws JobExecutionException {
    		try {
                 // 将当前对象包装为BeanWrapper
    			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                // 设置属性
    			MutablePropertyValues pvs = new MutablePropertyValues();
    			pvs.addPropertyValues(context.getScheduler().getContext());
    			pvs.addPropertyValues(context.getMergedJobDataMap());
    			bw.setPropertyValues(pvs, true);
    		}
    		catch (SchedulerException ex) {
    			throw new JobExecutionException(ex);
    		}
            // 子类实现该方法
    		executeInternal(context);
    	}
    
    	/**
    	 * Execute the actual job. The job data map will already have been
    	 * applied as bean property values by execute. The contract is
    	 * exactly the same as for the standard Quartz execute method.
    	 * @see #execute
    	 */
    	protected abstract void executeInternal(JobExecutionContext context) throws JobExecutionException;
    
    }
    

    调度器Scheduler绑定

    Scheduler绑定有两种方式,一种是使用bena的自动配置,一种是Scheduler手动配置。

    自动配置,这里演示SimpleScheduleBuilder

    @Configuration
    public class QuartzConfig {
    
        private static final String ID = "SUMMERDAY";
    
        @Bean
        public JobDetail jobDetail1() {
            return JobBuilder.newJob(FirstJob.class)
                    .withIdentity(ID + " 01")
                    .storeDurably()
                    .build();
        }
    
        @Bean
        public Trigger trigger1() {
            // 简单的调度计划的构造器
            SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
                    .withIntervalInSeconds(5) // 频率
                    .repeatForever(); // 次数
    
            return TriggerBuilder.newTrigger()
                    .forJob(jobDetail1())
                    .withIdentity(ID + " 01Trigger")
                    .withSchedule(scheduleBuilder)
                    .build();
        }
    }
    

    手动配置,这里演示CronScheduleBuilder

    @Component
    public class JobInit implements ApplicationRunner {
    
        private static final String ID = "SUMMERDAY";
    
        @Autowired
        private Scheduler scheduler;
    
        @Override
        public void run(ApplicationArguments args) throws Exception {
            JobDetail jobDetail = JobBuilder.newJob(FirstJob.class)
                    .withIdentity(ID + " 01")
                    .storeDurably()
                    .build();
            CronScheduleBuilder scheduleBuilder =
                    CronScheduleBuilder.cronSchedule("0/5 * * * * ? *");
            // 创建任务触发器
            Trigger trigger = TriggerBuilder.newTrigger()
                    .forJob(jobDetail)
                    .withIdentity(ID + " 01Trigger")
                    .withSchedule(scheduleBuilder)
                    .startNow() //立即執行一次任務
                    .build();
            // 手动将触发器与任务绑定到调度器内
            scheduler.scheduleJob(jobDetail, trigger);
        }
    }
    
    

    yml配置

    spring:
      # Quartz 的配置,对应 QuartzProperties 配置类
      quartz:
        job-store-type: memory # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。
        auto-startup: true # Quartz 是否自动启动
        startup-delay: 0 # 延迟 N 秒启动
        wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true
        overwrite-existing-jobs: false # 是否覆盖已有 Job 的配置
        properties: # 添加 Quartz Scheduler 附加属性
          org:
            quartz:
              threadPool:
                threadCount: 25 # 线程池大小。默认为 10 。
                threadPriority: 5 # 线程优先级
                class: org.quartz.simpl.SimpleThreadPool # 线程池类型
    #    jdbc: # 这里暂时不说明,使用 JDBC 的 JobStore 的时候,才需要配置
    

    SpringBoot自动配置中:spring.quartz对应的配置项定义在QuartzProperties中。

    主启动类

    @SpringBootApplication
    public class DemoSpringBootApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoSpringBootApplication.class, args);
        }
    }
    

    测试

    启动程序,FirstJob每5s执行一次,SecondJob每10s执行一次。

     [eduler_Worker-1] com.hyhwky.standalone.FirstJob           : FirstJob执行, 当前的时间: 2020-12-26 16:54:00
     [eduler_Worker-2] com.hyhwky.standalone.SecondJob          : SecondJob执行, 当前的时间: 2020-12-26 16:54:00
     [eduler_Worker-3] com.hyhwky.standalone.FirstJob           : FirstJob执行, 当前的时间: 2020-12-26 16:54:05
     [eduler_Worker-4] com.hyhwky.standalone.SecondJob          : SecondJob执行, 当前的时间: 2020-12-26 16:54:10
     [eduler_Worker-5] com.hyhwky.standalone.FirstJob           : FirstJob执行, 当前的时间: 2020-12-26 16:54:10
     [eduler_Worker-6] com.hyhwky.standalone.FirstJob           : FirstJob执行, 当前的时间: 2020-12-26 16:54:15
     [eduler_Worker-7] com.hyhwky.standalone.SecondJob          : SecondJob执行, 当前的时间: 2020-12-26 16:54:20
    

    Quartz持久化配置

    Quartz持久化配置提供了两种存储器:

    类型 优点 缺点
    RAMJobStore 不要外部数据库,配置容易,运行速度快 因为调度程序信息是存储在被分配给 JVM 的内存里面,所以,当应用程序停止运行时,所有调度信息将被丢失。另外因为存储到JVM内存里面,所以可以存储多少个 Job 和 Trigger 将会受到限制
    JDBC 作业存储 支持集群,因为所有的任务信息都会保存到数据库中,可以控制事物,还有就是如果应用服务器关闭或者重启,任务信息都不会丢失,并且可以恢复因服务器关闭或者重启而导致执行失败的任务 运行速度的快慢取决与连接数据库的快慢

    创建数据库表

    为了测试Quartz的持久化配置,我们事先在mysql中创建一个数据库quartz,并执行脚本,脚本藏在orgquartz-schedulerquartz2.3.2quartz-2.3.2.jar!orgquartzimpljdbcjobstore ables_mysql_innodb.sql,jdbcjobstore中有支持许多种数据库的脚本,可以按需执行。

    mysql> use quartz;
    Database changed
    mysql> show tables;
    +--------------------------+
    | Tables_in_quartz         |
    +--------------------------+
    | qrtz_blob_triggers       |## blog类型存储triggers
    | qrtz_calendars           |## 以blog类型存储Calendar信息
    | qrtz_cron_triggers       |## 存储cron trigger信息
    | qrtz_fired_triggers      |## 存储已触发的trigger相关信息
    | qrtz_job_details         |## 存储每一个已配置的job details
    | qrtz_locks               |## 存储悲观锁的信息
    | qrtz_paused_trigger_grps |## 存储已暂停的trigger组信息
    | qrtz_scheduler_state     |## 存储Scheduler状态信息
    | qrtz_simple_triggers     |## 存储simple trigger信息
    | qrtz_simprop_triggers    |## 存储其他几种trigger信息
    | qrtz_triggers            |## 存储已配置的trigger信息
    +--------------------------+
    
    

    所有的表中都含有一个SCHED_NAME字段,对应我们配置的scheduler-name,相同 Scheduler-name的节点,形成一个 Quartz 集群。

    引入mysql相关依赖

        <dependencies>
            <!-- 实现对 Spring MVC 的自动化配置 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!-- 实现对 Quartz 的自动化配置 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-quartz</artifactId>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
        </dependencies>
    
    

    配置yml

    spring:
      datasource:
        quartz:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/quartz?serverTimezone=GMT%2B8
          username: root
          password: 123456
      quartz:
        job-store-type: jdbc # 使用数据库存储
        scheduler-name: hyhScheduler # 相同 Scheduler 名字的节点,形成一个 Quartz 集群
        wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true
        jdbc:
          initialize-schema: never # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。
        properties:
          org:
            quartz:
              # JobStore 相关配置
              jobStore:
                dataSource: quartzDataSource # 使用的数据源
                class: org.quartz.impl.jdbcjobstore.JobStoreTX # JobStore 实现类
                driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
                tablePrefix: QRTZ_ # Quartz 表前缀
                isClustered: true # 是集群模式
                clusterCheckinInterval: 1000
                useProperties: false
              # 线程池相关配置
              threadPool:
                threadCount: 25 # 线程池大小。默认为 10 。
                threadPriority: 5 # 线程优先级
                class: org.quartz.simpl.SimpleThreadPool # 线程池类型
    
    

    配置数据源

    @Configuration
    public class DataSourceConfiguration {
    
        private static HikariDataSource createHikariDataSource(DataSourceProperties properties) {
            // 创建 HikariDataSource 对象
            HikariDataSource dataSource = properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
            // 设置线程池名
            if (StringUtils.hasText(properties.getName())) {
                dataSource.setPoolName(properties.getName());
            }
            return dataSource;
        }
    
        /**
         * 创建 quartz 数据源的配置对象
         */
        @Primary
        @Bean(name = "quartzDataSourceProperties")
        @ConfigurationProperties(prefix = "spring.datasource.quartz")
        // 读取 spring.datasource.quartz 配置到 DataSourceProperties 对象
        public DataSourceProperties quartzDataSourceProperties() {
            return new DataSourceProperties();
        }
    
        /**
         * 创建 quartz 数据源
         */
        @Bean(name = "quartzDataSource")
        @ConfigurationProperties(prefix = "spring.datasource.quartz.hikari")
        @QuartzDataSource
        public DataSource quartzDataSource() {
            // 获得 DataSourceProperties 对象
            DataSourceProperties properties = this.quartzDataSourceProperties();
            // 创建 HikariDataSource 对象
            return createHikariDataSource(properties);
        }
    
    }
    
    

    创建任务

    @Component
    public class JobInit implements ApplicationRunner {
    
        private static final String ID = "SUMMERDAY";
    
        @Autowired
        private Scheduler scheduler;
    
        @Override
        public void run(ApplicationArguments args) throws Exception {
            JobDetail jobDetail = JobBuilder.newJob(SecondJob.class)
                    .withIdentity(ID + " 02")
                    .storeDurably()
                    .build();
            CronScheduleBuilder scheduleBuilder =
                    CronScheduleBuilder.cronSchedule("0/10 * * * * ? *");
            // 创建任务触发器
            Trigger trigger = TriggerBuilder.newTrigger()
                    .forJob(jobDetail)
                    .withIdentity(ID + " 02Trigger")
                    .withSchedule(scheduleBuilder)
                    .startNow() //立即執行一次任務
                    .build();
            Set<Trigger> set = new HashSet<>();
            set.add(trigger);
            // boolean replace 表示启动时对数据库中的quartz的任务进行覆盖。
            scheduler.scheduleJob(jobDetail, set, true);
        }
    }
    
    

    启动测试

    启动测试之后,我们的quartz任务相关信息就已经成功存储到mysql中了。

    mysql> select * from qrtz_simple_triggers;
    +--------------+---------------------+---------------+--------------+-----------------+-----------------+
    | SCHED_NAME   | TRIGGER_NAME        | TRIGGER_GROUP | REPEAT_COUNT | REPEAT_INTERVAL | TIMES_TRIGGERED |
    +--------------+---------------------+---------------+--------------+-----------------+-----------------+
    | hyhScheduler | SUMMERDAY 01Trigger | DEFAULT       |           -1 |            5000 |             812 |
    +--------------+---------------------+---------------+--------------+-----------------+-----------------+
    1 row in set (0.00 sec)
    
    mysql> select * from qrtz_cron_triggers;
    +--------------+---------------------+---------------+------------------+---------------+
    | SCHED_NAME   | TRIGGER_NAME        | TRIGGER_GROUP | CRON_EXPRESSION  | TIME_ZONE_ID  |
    +--------------+---------------------+---------------+------------------+---------------+
    | hyhScheduler | SUMMERDAY 02Trigger | DEFAULT       | 0/10 * * * * ? * | Asia/Shanghai |
    +--------------+---------------------+---------------+------------------+---------------+
    
    

    源码下载

    本文内容均为对优秀博客及官方文档总结而得,原文地址均已在文中参考阅读处标注。最后,文中的代码样例已经全部上传至Gitee:https://gitee.com/tqbx/springboot-samples-learn,另有其他SpringBoot的整合哦。

    参考阅读

  • 相关阅读:
    无线鼠标换电池了
    Jython Interactive Servlet Console YOU WILL NEVER KNOW IT EXECLLENT!!! GOOD
    Accessing Jython from Java Without Using jythonc
    jython podcast cool isnt't it?
    Python里pycurl使用记录
    Creating an Interactive JRuby Console for the Eclipse Environment
    微软为AJAX和jQuery类库提供CDN服务
    Download A File Using Cygwin and cURL
    What is JMRI?这个是做什么用的,我真没看懂但看着又很强大
    用curl 发送指定的大cookie的http/https request
  • 原文地址:https://www.cnblogs.com/summerday152/p/14193968.html
Copyright © 2011-2022 走看看