• 集群式Quartz定时任务框架实践


     在日常开发汇总,经常会遇到需要定时任务的场景,简单的,可以使用Spring的定时任务调度框架,也可以使用Quartz。无论使用哪种,都需要解决一个问题,那就是集群问题。一般情况下,定时任务能且仅能运行于一台应用实例上。

    前提

    本文工程基于spring boot 2.1.7.RELEASE

     

    工程配置

     一、pom依赖

     

    如下图所示:

     

    二、yml配置

     

    yml配置如下图所示:

     

    三、quartz.properties

     

    quartz相关属性配置如图:

     

    注意:

    1、重中之重,要设置org.quartz.jobStore.isClustered=true,开启集群模式

    2、其它配置见释义

     

    四、调度器配置

     

    使用SchedulerFactoryBeanCustomizer个性化调度器

    @Configuration
    public class SysConfig {
    
        private final DataSource dataSource;
    
        public SysConfig(DataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        @Bean
        public SchedulerFactoryBeanCustomizer schedulerFactoryBeanCustomizer() {
            return (schedulerFactoryBean) -> {
                schedulerFactoryBean.setDataSource(dataSource);
                schedulerFactoryBean.setConfigLocation(new ClassPathResource("quartz.properties"));
            };
        }
    
    }

     

    五、系统启动

     

    系统启动,如显示quartz以cluster模式启动,则证明配置成功。如图所示:

     

    编写任务

     

    一、Job

     

    代码如下:

    package com.luas.quartz.cluster.demo.quartz.job;
    
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.scheduling.quartz.QuartzJobBean;
    
    public class UserJob extends QuartzJobBean {
    
        @Value("${server.port}")
        private String port;
    
        private String name;
    
        private Integer age;
    
        @Override
        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
            System.out.println(String.format("the user job is execute on port %s. it's name is %s, age is %s", port, name, age));
        }
    
        public String getPort() {
            return port;
        }
    
        public void setPort(String port) {
            this.port = port;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    }

     

    二、Controller触发

     代码如下:

    package com.luas.quartz.cluster.demo.controller;
    
    import com.luas.quartz.cluster.demo.quartz.job.UserJob;
    import org.quartz.*;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.time.LocalDateTime;
    
    @RestController
    @RequestMapping("/job")
    public class JobController {
    
        @Autowired
        private Scheduler scheduler;
    
        @RequestMapping("/user")
        public Object user() throws Exception {
            JobBuilder jobBuilder = JobBuilder
                    .newJob(UserJob.class)
                    .withIdentity(new JobKey("UserJob", "default"))
                    .withDescription("a demo quartz job")
                    .storeDurably();
    
            JobDataMap jobDataMap = new JobDataMap();
            jobDataMap.put("name", "luas");
            jobDataMap.put("age", 18);
    
            TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger()
                    .withIdentity(new TriggerKey("UserJob's trigger 1", "default"))
                    .withSchedule(createSimpleScheduleBuilder())
                    .forJob(new JobKey("UserJob", "default"))
                    .usingJobData(jobDataMap)
                    .startNow();
    
            this.scheduler.scheduleJob(jobBuilder.build(), triggerBuilder.build());
    
            return LocalDateTime.now();
        }
    
        private ScheduleBuilder createSimpleScheduleBuilder() {
            SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
                    .withIntervalInMilliseconds(10000)
                    .withRepeatCount(100);
    
            return simpleScheduleBuilder;
        }
    
    }

     

    三、系统启动

     

    依次启动8080、8082两个实例。idea默认只能启动一个实例,需要配置并行运行才可以运行两个相同的实例,如下图所示,勾选Allow parallel run即可。

     

    访问:http://localhost:8080/quartz/job/user,页面返回当前时间。

    此时,8080和8082控制台信息分别如图所示:

    8080:

     

    8082:

     

    可以看出,Quartz运行在8080实例上,而8082实例处于监控状态。

     

    四、切换运行实例

     

     停止8080实例,手工模拟服务器故障,观察8082实例控制台输出,发现quartz已经运行在8082实例上,如图所示:

     8080停止:

     

    8082接管:

     

     

    问题

     

    1、启动报错:Couldn't store job: JobDataMap values must be Strings when the 'useProperties' property is set.

     修改quartz.properties配置中org.quartz.jobStore.useProperties=false

     

     2、UserJob中@Value注解标注的port属性如何会自动注入、name、age属性如何会自动注入?

     参考QuartzAutoConfiguration、SpringBeanJobFactory类的如下片段:

     

    3、数据结构

     Quartz官网下载分发包,下载地址。下载完成后,在压缩包路径docsdbTables下,有各个数据库相对应的脚本文件,选择自己对应的脚本执行即可。如本文选择的为tables_mysql_innodb.sql。

     

    其它

    一、源码

     本文源码地址如下:

    • github:https://github.com/liuminglei/learning-demo/tree/master/quartz-cluster-demo

    • gitee:https://gitee.com/xbd521/learning-demo/tree/master/quartz-cluster-demo

    二、集成框架

     基于quartz的集群式、非集群式轻量封装定时任务调度框架-quartz、quartz-boot已共享,配置、操作更便捷、高效,欢迎star、fork。

     quartz-boot:

    • github:https://github.com/liuminglei/quartz-boot

    • gitee:https://gitee.com/xbd521/quartz-boot

     quartz

    • github:https://github.com/liuminglei/quartz

    • gitee:https://gitee.com/xbd521/quartz

     

    本文由【银河架构师】原创,转载请注明作者及出处。另附本文地址:

    https://www.cnblogs.com/luas/p/12040304.html

    微信搜索【银河架构师】,发现更多精彩内容。

    技术资料领取方法:关注公众号,回复微服务,领取微服务相关电子书;回复MK精讲,领取MK精讲系列电子书;回复JAVA 进阶,领取JAVA进阶知识相关电子书;回复JAVA面试,领取JAVA面试相关电子书,回复JAVA WEB领取JAVA WEB相关电子书。

     

     
  • 相关阅读:
    Python的网络编程[0] -> socket[1] -> socket 模块
    Python的网络编程[0] -> socket[0] -> socket 与 TCP / UDP
    Python的功能模块[4] -> pdb/ipdb -> 实现 Python 的单步调试
    Python的功能模块[3] -> binascii -> 编码转换
    Python的功能模块[2] -> abc -> 利用 abc 建立抽象基类
    Python的功能模块[1] -> struct -> struct 在网络编程中的使用
    Python的功能模块[0] -> wmi -> 获取 Windows 内部信息
    Python的程序结构[8] -> 装饰器/Decorator -> 装饰器浅析
    Python的程序结构[7] -> 生成器/Generator -> 生成器浅析
    Python的程序结构[6] -> 迭代器/Iterator -> 迭代器浅析
  • 原文地址:https://www.cnblogs.com/luas/p/12040304.html
Copyright © 2020-2023  润新知