【问题标题】:Spring Scheduler code within an App with multiple instances with multiple JVMs具有多个实例和多个 JVM 的应用程序中的 Spring 调度程序代码
【发布时间】:2019-01-15 08:57:21
【问题描述】:

我有一个使用fixedDelaycron 配置的spring 调度程序任务,并且该应用程序的多个实例在多个JVM 上运行。

默认行为是所有实例都在执行调度任务。

有没有一种方法可以控制这种行为,以便只有一个实例执行调度程序任务而其他实例不执行。

如果您知道任何方法,请告诉我。

谢谢

【问题讨论】:

  • 使用 Quartz 之类的东西来调度你的任务,而不是 Spring 中的简单调度器。 Quartz 允许您只制作单个实例的作业,并且当使用数据库存储触发器和执行逻辑时,这也适用于多个实例。只有一个会运行任务。另一种解决方案是仅在一个实例上启用任务异常并在其他实例上禁用调度。您可以有条件地对属性进行此操作。缺点是您可能会忘记为 1 个或多个实例禁用/启用此功能。

标签: spring spring-boot scheduled-tasks spring-batch


【解决方案1】:

我们遇到了类似的问题。我们这样修复它:

  1. 从我们的 Spring Boot 服务中删除了所有 @Scheduled bean。
  2. 创建的 AWS Lambda 函数按所需的时间表进行调度。
  3. Lambda 函数通过调度请求访问我们的顶级域。
  4. 负载平衡器将此请求转发到服务实例之一。

这样我们可以确保计划任务在我们的服务集群中只执行一次。

【讨论】:

  • 感谢您的回复。你能在春季环境中提出一些方法吗?
  • 不可能。如果您有服务实例集群,它们必须是无状态的。 @Scheduled 注解表示状态。
【解决方案2】:

我遇到过类似的问题,即相同的计划批处理作业在两台服务器上运行,而它打算一次在一个节点上运行。但后来我找到了一个解决方案,如果它已经在其他服务器上运行,则不执行该作业。

Job someJob = ...
Set<JobExecution> jobs = jobExplorer.findRunningJobExecutions("someJobName");
if (jobs == null || jobs.isEmpty()) {
        jobLauncher.run(someJob, jobParametersBuilder.toJobParameters());
    }
}

因此,在启动作业之前,需要检查作业是否已在其他节点上执行。 请注意,这种方法仅适用于基于数据库的作业存储库。

【讨论】:

  • 由于调度程序在不同的虚拟机上运行,​​如果它们在各自的虚拟机上同时到达“jobLauncher.run(....)”会怎样。它又回到了同样的问题,对吧?
【解决方案3】:

我们遇到了同样的问题,我们的三个实例运行相同的工作并且每天执行 3 次任务。我们通过使用 Spring 批处理解决了这个问题。 Spring Batch 只能具有唯一的作业 ID,因此如果您使用类似日期的作业 ID 开始作业,它将限制重复作业以相同的 ID 开始。在我们的例子中,我们使用了像 '2020-1-1' 这样的日期(因为它每天只运行一次)。所有三个实例都尝试使用 id '2020-1-1' 启动作业,但 spring 拒绝了两个重复的作业,说明作业 '2020-1-1' 已经在运行。

【讨论】:

  • 能否提供上述解决方案的代码sn-p?
【解决方案4】:

如果我对您的问题的理解是正确的,即您想在单个实例上运行此计划作业,那么我认为您应该查看ShedLock

ShedLock 确保您的计划任务最多同时执行一次。如果一个任务正在一个节点上执行,它会获取一个锁,以防止从另一个节点(或线程)执行相同的任务。请注意,如果一个任务已经在一个节点上执行,其他节点上的执行不会等待,直接跳过。

【讨论】:

    猜你喜欢
    • 2017-03-11
    • 1970-01-01
    • 1970-01-01
    • 2018-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-27
    相关资源
    最近更新 更多