多个实例上的Spring和计划任务

时间:2018-03-28 11:38:50

标签: spring spring-boot jobs job-scheduling

我们有一个Spring Boot应用程序,并且已安排任务。

我们希望在多个服务器上部署我们的应用程序,因此将是多个应用程序实例。

如何将Spring配置为仅在指定的服务器上运行计划任务?

5 个答案:

答案 0 :(得分:6)

这是一个非常广泛的话题。实现这一目标有很多选择。

  1. 您可以将应用程序配置为具有多个配置文件。例如,使用另一个个人资料' cron' 。并使用此配置文件仅在一台服务器上启动您的应用程序因此,例如,在生产环境中,您有三台服务器(S1,S2,S3),那么您可以在S1上运行配置文件prod和cron(-Dspring.profiles.active=prod,cron)。在S2和S3上只需使用prod配置文件(-Dspring.profiles.active=prod)。

    在代码中,您可以在调度程序类上使用@Profile("cron")。这样,只有当cron配置文件处于活动状态时才会执行

  2. 使用分布式锁。如果您的环境中有Zookeeper,则可以使用它来实现分布式锁定系统。

  3. 您可以使用某个数据库(mysql)并创建示例代码以锁定其中一个表并添加一个条目。无论哪个实例获得锁定,都会在此数据库中创建一个条目并执行cron作业。你需要 检查代码,如果getLock()成功,则继续执行。 Mysql有像LOCK TABLES这样的实用程序,你可以使用它来逃避并发读/写。

  4. 我个人会说,选项2是最好的。

答案 1 :(得分:6)

ShedLock 项目是专门为实现此目的而创建的。

依赖性-

<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>    

配置-

@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")

实施-

@Scheduled(cron = "0 0/15 * * * ?")
@SchedulerLock(name = "AnyUniqueName", 
  lockAtLeastForString = "PT5M", lockAtMostForString = "PT10M")
public void scheduledTask() {
    // ...
}

这将确保仅一个实例应运行计划的任务。

如果只希望特定实例运行Scheduler任务,

您需要配置调度程序以使用属性文件并像这样控制调度程序开关-

@ConditionalOnProperty(
  value = "scheduling.enabled", havingValue = "true", matchIfMissing = true
)
@Configuration
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
public class SchedulingConfig {

现在,您只需要为要运行Schedular的实例提供属性scheduling.enabled = true

答案 2 :(得分:2)

使用Spring执行此操作的最简单方法是使用环境变量和值注释:

1 - 在类中使用Value注释获取环境变量:

@Value("${TASK_ENABLED}")
private boolean taskEnabled;

2 - 检查taskEnabled值以执行任务:

@Scheduled(fixedDelay = 50000)
public void myTask() {
  if (this.taskEnabled) {
    //do stuff here...
  }
}

3 - 为每台服务器设置正确的环境变量:

假:

java -DTASK_ENABLED=0 -jar software.jar

真:

java -DTASK_ENABLED=1 -jar software.jar

全局配置类示例

要使用全局配置类,您应该说它是一个带有@Component的组件,并注释一个set方法将值传递给静态字段。

1 - 使用静态字段创建配置类:

@Component
public class AppConfiguration {

  public static boolean taskEnabled;

  @Value("${TASK_ENABLED}")
  public void setTaskEnabled(boolean taskEnabled) {
    this.taskEnabled = taskEnabled;
  }
}

2 - 检查taskEnabled值以执行任务:

@Scheduled(fixedDelay = 50000)
public void myTask() {
  if (AppConfiguration.taskEnabled) {
    //do stuff here...
  }
}

3 - 为每台服务器设置正确的环境变量:

假:

java -DTASK_ENABLED=0 -jar software.jar

真:

java -DTASK_ENABLED=1 -jar software.jar

答案 3 :(得分:0)

我认为您需要的帮助来自另一篇文章的一个答案。

请参阅此帖子:https://stackoverflow.com/a/65551473/4147392

答案 4 :(得分:0)

最简单的解决方案是您可以为不同的实例使用不同的属性文件。步骤如下

  1. 使用 @ConditionalOnProperty(prefix = "enable-scheduler", havingValue = "true") 注释您的调度程序类
  2. 在属性文件 enable-scheduler=true 中添加一个布尔值
  3. 现在对于任何实例使用 enable-scheduler=true,对于任何其他实例使用 enable-scheduler=false 在您的属性文件中。

示例:

@Component
@ConditionalOnProperty(prefix = "enable-scheduler", havingValue = "true")
public class AnyScheduler {

    private final Logger log = LoggerFactory.getLogger(getClass());

    private final AnyService service;

    @Autowired
    public AnyScheduler(AnyService service) {
        this.service = service;
    }

    @Scheduled(cron = "${scheduler-cron}")
    public void syncModifiedCve() {
        log.info("Scheduler started. . .");
        service.doTask();
    }

}