SpringBoot + Quartz分布式集群定时任务框架实践

转载请注明作者及出处:

作者:银河架构师

原文链接:https://blog.csdn.net/liuminglei1987/article/details/103556680

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

前提

本文工程基于spring boot 2.1.7.RELEASE

 

工程配置

 一、pom依赖

 

如下图所示:

SpringBoot + Quartz分布式集群定时任务框架实践_第1张图片

 

二、yml配置

 

yml配置如下图所示:

SpringBoot + Quartz分布式集群定时任务框架实践_第2张图片

 

三、quartz.properties

 

quartz相关属性配置如图:

SpringBoot + Quartz分布式集群定时任务框架实践_第3张图片

 

注意:

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 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即可。

SpringBoot + Quartz分布式集群定时任务框架实践_第4张图片

 

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

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

8080:

SpringBoot + Quartz分布式集群定时任务框架实践_第5张图片

 

8082:

SpringBoot + Quartz分布式集群定时任务框架实践_第6张图片

 

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

 

四、切换运行实例

 

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

 8080停止:

SpringBoot + Quartz分布式集群定时任务框架实践_第7张图片

 

8082接管:

 

SpringBoot + Quartz分布式集群定时任务框架实践_第8张图片

 

问题

 

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类的如下片段:

SpringBoot + Quartz分布式集群定时任务框架实践_第9张图片

SpringBoot + Quartz分布式集群定时任务框架实践_第10张图片

 

3、数据结构

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

SpringBoot + Quartz分布式集群定时任务框架实践_第11张图片

 

其它

一、源码

 本文源码地址如下:

  • 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

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

 

 

你可能感兴趣的:(日积月累)