spring quartz定时任务集群环境下如何实现只在单个节点运行

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring quartz定时任务集群环境下如何实现只在单个节点运行相关的知识,希望对你有一定的参考价值。

比如我一个系统部署在两个tomcat下, 但是我只想指定任务在其中一个tomcat下执行任务,另一个tomcat不执行任务。两个tomcat下边是同样的spring+quartz配置。
如何能做到呢?求大神帮忙,谢谢!!!!
JavaWeb项目

建议使用redis设置一个标志位,如果其中一台跑完了,那么就设置标志位为true,记住一定要 把发起跑批的间隔设置长一点,否则会有问题。。。
这种只能通过分布式锁这种全局设置的思路去控制,spring本身没有这个机制。
参考技术A 把其中某个tomcat的定时任务配置取消掉?

集群式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

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

 技术图片

 

以上是关于spring quartz定时任务集群环境下如何实现只在单个节点运行的主要内容,如果未能解决你的问题,请参考以下文章

spring boot 整合 quartz 集群环境 实现 动态定时任务配置原

集群环境下定时调度的解决方案之Quartz集群

Spring+quartz集群配置,Spring定时任务集群,quartz定时任务集群

Spring整合Quartz定时任务 在集群分布式系统中的应用(Mysql数据库环境)

Java技术分享:集群环境下的定时任务

集群环境下定时任务调度问题与方案探讨